HUProxy or How to Bypass Internet censorship

Some networks only have restricted access to the internet or run MITM on the encryption to scan the traffic. In the most cases it is possible to setup a secure proxy connection to bypass this restrictions.

This guide shows how to use HUProxy to connect to a remote SSH server and setup a socks5 proxy to bypass any censorship and MITM attacks.

Requirements

The following things are required:

  • reachable from public internet
  • linux server with docker

To make the usage easier a domain or subdomain should be used instead of the IP address. This guide uses huproxy-test.boehmke.net as domain.

Important: Because HUProxy does not support any authentication or encryption it is required to setup HUProxy behind a reverse proxy that also handles the authentication via basic auth.

This guide uses Traefik as reverse proxy with automatic TLS certificate request from Let’s Encrypt.

Build HUProxy image

Currently HUProxy is not available as prebuild docker image. So the first step is to build the docker image.

First clone the HUProxy repo

git clone https://github.com/google/huproxy.git

then enter the cloned directory

cd huproxy

and as last step build the image

docker build -t local/huproxy .

Setup Server

The following docker-compose.yml shows a simple setup including SSL and basic auth:

version: "3"

volumes:
  traefik_data:

services:
  traefik:
    image: traefik:latest
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    command:
      - --entryPoints.web.address=:80
      - --entryPoints.websecure.address=:443

      - --providers.docker
      - --providers.docker.exposedByDefault=false

      - --certificatesresolvers.letsencrypt.acme.httpChallenge.entryPoint=web
      - --certificatesresolvers.letsencrypt.acme.email=admin@example.com
      - --certificatesresolvers.letsencrypt.acme.storage=/data/acme.json

    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - traefik_data:/data/

  huproxy:
    image: local/huproxy
    restart: unless-stopped
    command: "/huproxy --listen :8086"

    labels:
      traefik.enable: "true"
      traefik.http.routers.huproxy.rule: "Host(`huproxy-test.boehmke.net`)"
      traefik.http.routers.huproxy.entrypoints: "websecure"
      traefik.http.routers.huproxy.middlewares: "huproxy-auth@docker"
      traefik.http.routers.huproxy.tls.certresolver: "letsencrypt"
      traefik.http.routers.huproxy.tls.options: "default"
      traefik.http.services.huproxy.loadbalancer.server.port: "8086"
      traefik.http.middlewares.huproxy-auth.basicauth.users: "proxy:$apr1$4rgk5ce9$LR8iGRL7hq8By5vc6/tLu1"

In this example a user proxy with the password proxy is used. To run the proxy simply start the applications with docker-compose up -d.

Setup Client

Note: this requires a working go compiler

To connect to the client the huproxyclient is needed.

To build the client clone the HUProxy repo (or reuse the one from Build HUProxy image)

git clone https://github.com/google/huproxy.git

then enter the cloned directory

cd huproxy/huproxyclient

Then build the client

# build for native system
go build -v .

# build for windows
GOOS=windows GOARCH=amd64 go build -v .

The directory now contains the build application huproxyclient (or huproxyclient.exe for windows). This application can now be used on a system with a blocked internet connection.

To connect to an SSH server at ssh.example.com run

ssh -o "ProxyCommand=./huproxyclient -auth=proxy:proxy wss://huproxy-test.boehmke.net/proxy/%h/%p" ssh.example.com

this way the connection to ssh.example.com is created via the huproxy server at huproxy-test.boehmke.net.

For an easier reuse it is also possible to add the ProxyCommand for the ~/.ssh/config

IPv6 NAT & Docker

In the past the only way to get a working IPv6 NAT setup for docker was the great docker-ipv6nat companion container initiated by Robbert Klarenbeek.

Since some month now there is also support for IPv6 NAT in the docker daemon that can be used like the IPv4 NAT. Currently this feature is still marked as experimental and there are still some open issue (see below).

This post should give a basic example how to use the IPv6 NAT of the docker daemon and also document a part of the development. It also list issues and fixes that in the progress to get this feature ready for productive.

Enable IPv6 NAT

By default the creation of ip6tables rules is disable to keep backward compatibility. Additional it is also required to enable IPv6 and set a private IPv6 network for the fixed-cidr-v6 in the daemon or in a user-defined network. The second step is exactly the same as for the docker-ipv6nat image.

To enable IPv6 NAT on the default docker network add the following to the /etc/docker/daemon.json (adapt the fixed-cidr-v6 as needed):

{
    "experimental": true,
    "ip6tables": true,
    "fixed-cidr-v6": "fd00:dead:beef::/48",
    "ipv6": true
}

To use IPv6 NAT for user defined networks simply create a subnet with enabled IPv6:

docker network create --ipv6 --subnet fd00:dead:beef::/48 mynetwork

In this case you only have to add the following to the /etc/docker/daemon.json:

{
    "experimental": true,
    "ip6tables": true
}

Thats all. Simply start a random container and expose a port and it should be reachable via IPv6.

History

There are a lot of request for IPv6 NAT in the docker daemon. One of the most popular one is moby#25407. Because this feature was not implemented for a long time the docker-ipv6nat project was created.

In 2017 wrridgwa created a Merge Request libnetwork#2023 for libnetwork that implements the creation of ip6tables rules. Sadly this Merge Request was never merged and get outdated after some time.

In July 2020 I reworked this Merge Request to get it working with the latest version of libnetwork and published the changes as libnetwork#2572. After some month and multiple requested changes the Merge Request gets merged end of october. (Vendor MR: moby#41604)

Finally the new --ip6tables config was added to the daemon in moby#41622 as an experimental option. Also cli#2846 updated the documentation for new option.

Sadly the modifications of the docker daemon caused some new issues:

With release 20.10.6 (2021-04-12) all of this issues should be resolved.

Initial post & HowTo Hugo

This is the first post here. So it is not completely useless I will use this post as a manual for the blog itself.

Hugo

The blog is build with hugo. Hugo is a static site generator that generates a static HTML website from a set of markdown files.

The page is build with a single command:

hugo

The generated site can now be found in public/.

For development purpose the command

hugo server -D

can be used to start a local web server which makes the website available at http://127.0.0.1:1313/. This pages get automatically updated and reloaded for any changed markdown or template file.

The -D options does also show draft posts which are normally not rendered in for a production build.

Terminal theme

Based on my limited web design skills it was required to use an existing hugo theme. I selected the terminal from panr.

The documentation suggests to use git submodule to add the theme to repository of the blog. This would only add a reference to the theme and requires that the repository of the theme is accessible to build the website. I decided to use git subtree instead which will add the content of the theme repository directly into the blog repository and provides a way to updating them.

As first step the theme repository is added as remote terminal_theme:

git remote add terminal_theme https://github.com/panr/hugo-theme-terminal.git

Then the theme is added as subtree:

git subtree add --prefix themes/terminal terminal_theme master --squash

In this case the theme is stored in themes/terminal and received from the master branch. Instead of master also a tag can be used. The --squash will squash all commits into a single one.

As last step simply set the theme in the config.yml to terminal and configure the theme as documented in the readme.

To update the theme a modified subtree command is used:

git subtree pull --prefix themes/terminal terminal_theme master --squash

Mermaid JS

Mermaid JS is a javascript library to generate diagrams and charts with text based definitions. To make the usage with Hugo it is integrated with custom Shortcode mermaid.

The following example:

{{<mermaid>}}
graph TD;
  A-->B;
  A-->C;
  B-->D;
  C-->D;
{{</mermaid>}}

will generate this diagram:

graph TD; A-->B; A-->C; B-->D; C-->D;