📜 ⬆️ ⬇️

Experience masking OpenVPN-tunnel using obfsproxy

Preamble


In connection with the emerging trends, I decided to obfuscate my modest OpenVPN tunnel, just to get my hand — you never need it ...

It is given: cheap VPS with white IP, working under Ubuntu Trusty Server Edition and serving OpenVPN server.
Required: if possible, hide the OpenVPN tunnel, preferably without the invention of bicycles.

obfsproxy


Since the Tor-node has already been raised on the VPS, the idea of obfsproxy came by itself. As it turned out, the utility really knows how to pretend to be SOCKS-proxy, which means obfuscate not only Tor connections, but also virtually arbitrary traffic. Rummaging around on the web, I found several manuals, but many were either outdated or out of sight the necessary details.
Important! Versions.
  • OpenVPN versions earlier than 2.3.4 contains a bug that interferes with normal work with SOCKS proxies. If you want to use the socks mode, make sure that you have the appropriate version of OpenVPN installed.
  • Apparently, obfsproxy is still not included in the Tor repository, and the Debian / Ubuntu repositories may contain old versions without scramblesuit support. In this case, obfsproxy must be installed using the pip utility ( python-pip package):
    pip install obfsproxy 

    After the installation is completed, make sure that you are using the scramblesuit version of obfsproxy:
      obfsproxy scramblesuit -h 

  • The obfsproxy version for Windows can be extracted from the Tor Browser package by finding the Tor \ PluggableTransports subdirectory.


From the man obfsproxy you can learn the following
  obfsproxy [logging_options] [--data-dir path] transport [--dest destination] [--password BASE32PASS] mode listen_addr 

A little more:
  • --data-dir statepath - directory for storing temporary data. Required if obfsproxy is not in managed mode.
  • transport - selects the method of traffic obfuscation and the mode of its transmission. We are interested in the following transports:
    • scramblesuit is a relatively new method that we will use.
    • dummy is a debugging method in which traffic is transmitted as is, without obfuscation.

  • mode - selects the proxy mode. We are interested in the following modes:
    • server - accepts client connections, deobfusts and redirects to the destination address. This mode is useful on the server side.
    • client - transparent proxying: accepts data, obfusts and sends to the server at the destination address. That we will use on the client side.
    • socks - imitation of socks-proxy.

    The difference between client and socks modes is that in the first case the address of the remote host the target application is trying to connect to must be replaced with the address of the opbfsproxy client, while in the second case the target application uses obfsproxy as SOCKS proxy. For our purposes, and the client.
  • --dest destination - for the server: sets the address-to-address pair to which deobfuscated connections will be redirected. For client: sets the address of the obfsproxy server.
  • --password BASE32PASS - set the key (pre-shared key) for the scramblesuit method. The key must be a string of at least 20 characters encoded in base32 and match on the client and on the server. A suitable key can be generated as follows:
     python -c "import os,base64; print base64.b32encode(os.urandom(20))" 

  • --password-file filepath is an alternative method for setting the key for the scramblesuit method, in case you do not want to write the pre-shared key in clear text.
  • log_options includes the following keys:
    • --log-file path - sets the location of the log.
    • --log-min-severity level - What level events (debug, info, warning, error) to record.
    • --no-safe-logging - by default obfsproxy cuts addresses from the log, but this key allows you to change this behavior.
    • --no-log - disables logging altogether (by default).


The VPN server itself can either be hidden behind obfsproxy completely (by making you listen to 127.0.0.1), or you can leave it on the external network interface in case of obfsproxy failure. I chose the second option as more reliable.
Thus, to run obfsproxy on the server side, it suffices to perform something like:
 obfsproxy --log-file /var/log/obfsproxy-openvpn.log --log-min-severity info scramblesuit --password BASE32LONGPASS --dest 1.2.3.4:1800 server 1.2.3.4:81 

where 1.2.3.4 is the external IP of the host, 1800 is the port that OpenVPN listens to, 81 is the port that obfsproxy will listen to.
A serious drawback is the inability of obfsproxy to run in daemon mode, but this can be circumvented.

Running on the client is almost identical to the server:
 obfsproxy scramblesuit --password BASE32LONGPASS --dest 1.2.3.4:81 {client|socks} 127.0.0.1:81 

where 1.2.3.4:81 is the address that the obfsproxy server listens to, 127.0.0.1:81 is the address that the client listens to.
')

OpenVPN setup


Important : OpenVPN must use the TCP protocol.
If we use client mode, we simply replace the address of the remote server in the openvpn configuration file with the address of the local obfsproxy:
 #remote 1.2.3.4 1800 #    remote 127.0.0.1 81 #       obfsproxy 

Socks mode
To work in socks mode, we configure OpenVPN a little bit differently:
 #remote 1.2.3.4 1800 #    remote 1.2.3.4 81 #  obfsproxy- (!    ) socks-proxy-retry socks-proxy 127.0.0.1 81 #  socks proxy 

Considering the need to replace the address of the OpenVPN server with the address of the obfsproxy server, I do not see any particular benefit from the proxy mode — applications in which the connection addresses are tightly protected are still not zaproksirue.

If openvpn traffic is blocked only occasionally (for example, only on the working network), you can use the following technique. In the OpenVPN configuration file, write the following:
 <connection> remote 1.2.3.4 1800 #    </connection> <connection> remote 127.0.0.1 81 #  ,   obfsproxy </connection> 

OpenVPN will try to use each <connection> section in turn, until it makes a successful connection.

Regarding the launch of obfsproxy, unfortunately, OpenVPN itself does not support pre-connect scripts (running the command before attempting to connect). You can try to install one of the unofficial patches , run obfsproxy manually, or use one of the following methods.

openvpn-gui for windows


If you use the openvpn-gui wrapper to connect manually (or automatically with the --connect key), then the easiest thing to do is to create the XXXXXX_pre.bat file in the config folder, where XXXXXX is the name of the configuration file (without the .ovpn extension). This file will be automatically executed before openvpn-gui tries to connect. The contents of the file will look something like this:
 start "window title" /MIN "%USERPROFILE%\Tor Browser\Tor\PluggableTransports\obfsproxy.exe" --log-min-severity info --data-dir "%TEMP%\obfs-openvpn" scramblesuit --password-file obfsproxy.key --dest 1.2.3.4:81 client 127.0.0.1:81 


Using the start command (without the / wait key, it works as an analogue of the & operator in the shell) is necessary, since openvpn-gui waits for the pre-up script to complete before attempting to connect. The disadvantage of this method is a console window that constantly hangs in the background. If desired, it can be hidden by the utility hidcon or similar.

obfsproxy as a service


If OpenVPN is launched as a service, then it makes sense to organize the launch of obfsproxy in the same way.
You can create a service using the MS srvany utility or some alternative utility like NSSM . The question of running an application as a Windows service is beyond the scope of the article, so I’ll just point out that this approach will allow you to set dependencies on other services, thereby ensuring that obfsproxy will be available at the time OpenVPN is launched.

This approach can be combined with the previous one, using the bat-file launched by openvpn-gui, to start the created service with the net start command.

Effect on speed


The last test was carried out on February 7, 2016 through the good old speedtest.net using the same server in London (the OpenVPN server is hosted in Latvia, the client is in the European part of Russia) from a Windows 7 client machine. Measurements were made in tenfold repetition. From the results it follows that, at least in my case, obfsproxy does not have a significant effect on speed. So, if there is a good channel to your VPN server, it will not be worse.

ModePing msDownload, MB / sUpload, MB / s
directthe average5620.1821.03
coeff. variations3.6%0.6%1.2%
openvpnthe average5620.2121.29
coeff. variations2.3%1.0%1.0%
openvpn + obfsproxythe average5520.2421.15
coeff. variations4.0%0.73%1.34%


Run obfs-proxy as init.d service


init.d / obfs-openvpn
#! / bin / sh
### BEGIN INIT INFO
# Provides: obfsproxy-openvpn
# Required-Start: $ remote_fs $ syslog
# Required-Stop: $ remote_fs $ syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: obfsproxy wrapper for openvpn
# Description: This file starts up obfsproxy configured to process obfuscated openvpn client connections.
### END INIT INFO

# Author: Vindicar <the.vindicar@gmail.com>

# Do NOT "set -e"

# PATH should only include / usr / * if it runs after the mountnfs.sh script
PATH = "/ sbin: / usr / sbin: / bin: / usr / bin: / usr / local / bin"
DESC = "obfsproxy wrapper for openvpn"
NAME = obfs-openvpn
DAEMON = "/ usr / local / bin / obfsproxy"

OBFSPROXY_KEYFILE = "/ etc / obfsproxy-openvpn.key"
SERVER_IP =?.?.?.? # <<< TODO: insert server address here
OPENVPN_PORT = 443 #port 443 due to use of sslh
OBFSPROXY_PORT = 81

DAEMON_ARGS = "- log-file /var/log/obfs-openvpn.log --log-min-severity warning \
--data-dir / tmp / obfs-openvpn \
scramblesuit --password-file $ OBFSPROXY_KEYFILE \
--dest $ SERVER_IP: $ OPENVPN_PORT server $ SERVER_IP: $ OBFSPROXY_PORT "
PIDFILE = / var / run / $ NAME.pid
SCRIPTNAME = / etc / init.d / $ NAME

# Exit if the package is not installed
[-x "$ DAEMON"] || exit 0

# Read configuration variable file if it is present
[-r / etc / default / $ NAME] &&. / etc / default / $ NAME

# Load the rcS variables
. /lib/init/vars.sh

# Define LSB log_ * functions.
# Depend on lsb-base (> = 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. / lib / lsb / init-functions

#
# Function that starts the daemon / service
#
do_start ()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile $ PIDFILE --exec $ DAEMON --test> / dev / null \
|| return 1
start-stop-daemon - start - background - quiet - make-pidfile - pidfile $ PIDFILE - exe $ DAEMON - \
$ DAEMON_ARGS \
|| return 2
# Add code here, if necessary, that waits
# to handle requests from services started
# on this one. As a last resort, sleep for some time.
}

#
# Function that stops the daemon / service
#
do_stop ()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
# EDIT: removed --name parameter since ssd matches
# will be relying on the pidfile to stop it.
start-stop-daemon --stop --quiet --retry = TERM / 30 / KILL / 5 --pidfile $ PIDFILE
RETVAL = "$?"
["$ RETVAL" = 2] && return 2
# Wait for children to finish
# and if the daemon is from ever in this script.
# If the conditions above are not satisfied then add some other code
# that waits
# needed by services started subsequently. A last resort is to
# sleep for some time.
# start-stop-daemon --stop --quiet --oknodo --retry = 0/30 / KILL / 5 --exec $ DAEMON
# ["$?" = 2] && return 2
# Many daemons don't exit their pidfiles when they exit.
rm -f $ PIDFILE
return "$ RETVAL"
}

#
# Function that sends a SIGHUP to the daemon / service
#
do_reload () {
#
# If the daemon can reload configuration without
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
start-stop-daemon --stop --signal 1 --quiet --pidfile $ PIDFILE
return 0
}

case "$ 1" in
start)
["$ VERBOSE"! = No] && log_daemon_msg "Starting $ DESC" "$ NAME"
do_start
case "$?" in
0 | 1) ["$ VERBOSE"! = No] && log_end_msg 0 ;;
2) ["$ VERBOSE"! = No] && log_end_msg 1 ;;
esac
;;
stop)
["$ VERBOSE"! = No] && log_daemon_msg "Stopping $ DESC" "$ NAME"
do_stop
case "$?" in
0 | 1) ["$ VERBOSE"! = No] && log_end_msg 0 ;;
2) ["$ VERBOSE"! = No] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$ DAEMON" "$ NAME" && exit 0 || exit $?
;;
#reload | force-reload)
#
# Leave this commented out
# and leave 'force-reload' as an alias for 'restart'.
#
#log_daemon_msg "Reloading $ DESC" "$ NAME"
#do_reload
#log_end_msg $?
# ;;
restart | force-reload)
#
# If the "reload" option is then removed the
# 'force-reload' alias
#
log_daemon_msg "Restarting $ DESC" "$ NAME"
do_stop
case "$?" in
0 | 1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
#echo "Usage: $ SCRIPTNAME {start | stop | restart | reload | force-reload}"> & 2
echo "Usage: $ SCRIPTNAME {start | stop | status | restart | force-reload}"> & 2
exit 3
;;
esac

Source: https://habr.com/ru/post/251569/


All Articles