Skip to content

An abstraction around the set of tools to bypass internet censorship in extremely restricted regions.

License

Notifications You must be signed in to change notification settings

Soberia/bypasshub

Repository files navigation

💡 About

bypasshub is an abstraction around the set of tools to bypass internet censorship in extremely restricted regions such as Iran or China.

The goal of the project is to minimize the work needed to configure required stuff to get things done for the end user. It's also tried to follow the best practices to honor both security and performance.

⚙️ How It Works

                                         ____________ 
                                        | Xray-core  |
                         -------------> |Proxy Server| ---------------
                        ¦               |____________|                ¦
                        ¦                     ¦                       ¦
                        ¦                     ¦                       ¦
                        ¦                     ˅                       ˅
                     _______              __________              __________
                    |       |            |   BIND   |            |          |
 Client ----------> | NGINX | ---------> |DNS Server|            | Internet |
                    |_______|            |__________|            |__________|
                        ¦                     ˄                       ˄
                        ¦                     ¦                       ¦
                        ¦                     ¦                       ¦
                        ¦                ____________                 ¦
                        ¦               | OpenConnect|                ¦
                         -------------> | VPN Server |----------------
                                        |____________|

bypasshub just consists of a bunch of Docker containers.

NGINX role is an entry point for all the incoming connections. From there, the incoming connection gets redirected to its destination based on the destination port. For TLS-based connections on TCP ports, the connection is routed based on specified SNI value and if there is no match, a dummy webpage will be returned instead to mimic a real web server's behavior when confronted with national firewall active probing.

Currently, Xray-core proxy and OpenConnect VPN servers are available. The clients of these services are isolated and can't communicate with each other or with other containers on the network with an exception for sending the DNS queries to the BIND caching DNS server.

All of the containers run as a non-privileged user except the OpenConnect.

📋 How to Use

Make sure you've installed the Docker with Compose CLI V2 support. Then, clone the repository and build the services:

git clone https://github.com/Soberia/bypasshub.git
cd bypasshub && docker compose build

Fill the following parameters in the config file with your information:

Note
It's also recommended to change XRAY_SNI, XRAY_CDN_SNI and OCSERV_SNI subdomain part to something else. For example, default XRAY_SNI value is xr.$DOMAIN, you can change it to hotdog.$DOMAIN or any other random value instead. You'll use these values when connecting from the client side.

You need to go to your domain registrar and set the nameservers to the ns1.$DOMAIN and ns2.$DOMAIN (replace $DOMAIN with your actual domain, e.g. ns1.domain.com). You also need to create glue records for these nameservers you just defined. The glue records for your nameservers should point to your server's public IP address. (i.e. ns1.$DOMAIN -> $PUBLIC_IPV4)

However, if you already have a DNS server on your machine or you use your DNS registrar's, you can skip the above step and enable ENABLE_CERTBOT_HTTP_MODE and disable ENABLE_AUTHORITATIVE_ZONE parameters because you can't use two DNS servers at the same time on a same port. On your DNS server, create an A (and/or AAAA) record for DOMAIN, XRAY_SNI, XRAY_CDN_SNI, OCSERV_SNI and www.$DOMAIN and point them to the PUBLIC_IPV4 (or NGINX_IPV6).

Note
If you use the Cloudflare DNS server, you can go to the My Profile > API Tokens of your dashboard and create a new API token for editing the DNS zone and specify your token with the CLOUDFLARE_API_TOKEN parameter. In this way, a wildcard TLS certificate will be generated instead and there is no need to regenerate the certificate again when the SNI values have been changed.
Both ENABLE_AUTHORITATIVE_ZONE and ENABLE_CERTBOT_HTTP_MODE parameters should be disabled for this to take effect.

Warning
You need to stop any service that you might have listening on the TCP port 80 if the ENABLE_CERTBOT_HTTP_MODE parameter is enabled.

Warning
You may need some time for your nameservers to get populated before you continue.

Now, bring the containers up:

docker compose up -d

Managing Users

See the user management page.

Connecting from Client

Get the Xray-core and OpenConnect clients for your devices.

  • For Xray-core, in your client, add a subscription with the following URL with the user's credentials:

    https://$DOMAIN:$TLS_PORT/subscription?username=USERNAME&uuid=PASSWORD
    
    Adding the Server Manually

    Use these values when adding a new server in your client:

    Protocol: VLESS
    Address: $XRAY_SNI
    Port: $TLS_PORT
    UUID: PASSWORD
    Flow: xtls-rprx-vision
    Transport Protocol: TCP
    Security: TLS
    

    or if you want to connect via the CDN:

    Protocol: VLESS
    Address: $XRAY_CDN_SNI (or IP addresses of your CDN provider)
    Port: $CDN_TLS_PORT
    UUID: PASSWORD
    Transport Protocol: WS
    Transport Host: $XRAY_CDN_SNI
    Transport Path: /ws?ed=2560
    Security: TLS
    SNI: $XRAY_CDN_SNI
    
    Using IP Address Instead of Domain

    If your domain already is blocked, change XRAY_SNI parameter's value to something else (e.g. google.com) and rebuild the containers and change the following values to connect with the IP address instead: (replace PUBLIC_IPV4 with NGINX_IPV6 if you want to use IPv6)

    Address: $PUBLIC_IPV4
    SNI: $XRAY_SNI
    

    You may also need to tell your client to allow insecure connections.

  • For OpenConnect, you can connect to the server with the following command: (here's on Windows client)

    echo PASSWORD| openconnect.exe ^
        --non-inter ^
        --passwd-on-stdin ^
        --interface wintun ^
        --user USERNAME ^
        --server $OCSERV_SNI:$TLS_PORT

    If you're unable to establish a successful DTLS connection, you can append the --no-dtls argument for a faster initial connection.

    Note
    If OCSERV_KEY parameter is set, --server argument should contain the secret key as a query string:

    --server $OCSERV_SNI:$TLS_PORT/?$OCSERV_KEY
    Using IP Address Instead of Domain

    If your domain already is blocked, change OCSERV_SNI parameter's value to something else (e.g. bing.com) and rebuild the containers and append the following arguments to connect with the IP address instead: (replace PUBLIC_IPV4 with NGINX_IPV6 if you want to use IPv6)

        --sni $OCSERV_SNI ^
        --resolve $OCSERV_SNI:$PUBLIC_IPV4

    After reconnecting with added arguments, you also need to add the printed fingerprint token as the --servercert argument:

        --servercert pin-sha256:Q8CaEEFJqy2xyD9+SsAwfMuVH7jz/Rq0r/HXmNkIg9k=

Using a CDN

If your server's IP address (or certain ports) is blocked or traffic to your server gets throttled by the national firewall, you might be able to access your server again or improve the connection speed by placing your server behind a CDN. However, only Xray-core can benefit from this due to the fact that usually the CDN providers don't offer tunnel at the TCP/UDP level on their free plans. The Xray-core is able to work on a WebSocket or gPRC connection and a CDN provider like Cloudflare supports both of these protocols for free.

The following will demonstrate how to place your server behind the Cloudflare CDN, but the instructions should be the same for other providers:

  • Login to your dashboard.
  • Add your website and if your current DNS records couldn't be detected correctly, from the DNS panel, create an A (and/or AAAA) record for DOMAIN, XRAY_SNI, XRAY_CDN_SNI, OCSERV_SNI and www.$DOMAIN and point them to the PUBLIC_IPV4 (or NGINX_IPV6). The Proxy status should be enabled for all except for the XRAY_SNI and OCSERV_SNI. You'll need to swap your nameservers to the Cloudflare's in your domain registrar and also remove the glue records.
  • From the SSL/TLS panel, change the encryption mode to Full or Full (strict). In the Edge Certificates section, enable the TLS 1.3 option and set the Minimum TLS Version option to the TLS 1.3.
  • In the Network panel, make sure the WebSockets is enabled.
  • If the ENABLE_XRAY_SUBSCRIPTION parameter is enabled, you should set the Caching Level option to the No query string in the Configuration section of Caching panel.
  • The ENABLE_AUTHORITATIVE_ZONE and ENABLE_XRAY_CDN parameters should be disabled and enabled respectively. Rebuild the containers after the modification.
  • Update your Xray-core client configurations to reflect the changes. If you can't make a successful connection, try with an IP scanner to find healthy IP addresses. You can also put these IP addresses that work on different ISPs to the xray/configs/cdn-ips to publish them on the subscription:
    1.1.1.1 ISP#1
    1.1.1.2 ISP#2
    
  • (Optional) Now, you're able to connect to the server either with the CDN or directly. But if you only need to connect via the CDN, you might want to remove the DNS records for the XRAY_SNI and OCSERV_SNI to hide your server's IP address from the national firewall.

Dummy Website

The TLS-based services like Xray-core and OpenConnect are camouflaged behind a web server which decides the destination of incoming traffic based on the passed SNI value.

By default, for invalid requests (or authentication failures in the case of Xray-core) an empty index.html template located in nginx/static directory will be returned. You should modify it up to the point to represent a good unique indistinguishable fake webpage. If you need to include static assist like JavaScript, CSS or images, you can place them in the same mentioned directory.

Warning
OpenConnect VPN server can be detected through the exposed SNI. It's also recommended to set OCSERV_KEY parameter to hide the server identity.

Additional Configurations

For Xray-core you can place additional configuration files in the xray/configs directory (e.g. to block BitTorrent traffic).

For OpenConnect, you can place user-based configuration files in the ocserv/configs directory (e.g. to give the user a static IP address). The name of the config file should correspond to the defined username.

DNSSEC

For securing the exchanged data for your authoritative DNS zone, you can enable the ENABLE_DNSSEC parameter (ENABLE_AUTHORITATIVE_ZONE also should be enabled) and restart the container to generate the keys:

docker compose restart bind --no-deps

and get the keys by running the following command and setting them on your parent domain through your domain registrar:

docker compose exec bind \
    bash -c "dig @localhost dnskey $DOMAIN | dnssec-dsfromkey -Af - $DOMAIN"

IPv6

Enable the ENABLE_IPV6 parameter and you can either specify your server's Global Unicast IPv6 address prefix with IPV6_PREFIX parameter or fill the rest of IPv6 parameters manually.

You may also need the following firewall rules in the FORWARD chain: (they won't be permanent)

ip6tables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT # Only needed if doesn't already exist
ip6tables -A FORWARD -p ipv6-icmp -j ACCEPT # Only needed if doesn't already exist
ip6tables -A FORWARD -d $IPV6_SUBNET -j ACCEPT
ip6tables -A FORWARD -s $IPV6_SUBNET -o eth0 -j ACCEPT

Logs

The persistence of the logs can be controlled by the NGINX_LOG_PURGE_INTERVAL parameter.
You can see the logs by running the following commands:

docker compose exec nginx cat /tmp/nginx/log/access.log # Clients access log
docker compose exec nginx cat /tmp/nginx/log/static.log # Dummy website access log
docker compose exec nginx cat /tmp/nginx/log/api.log # API access log
docker compose exec nginx cat /tmp/nginx/log/error.log

Update Containers

Stop and remove the current containers:

docker compose down --rmi all

Get the latest source:

git stash && \
    git pull && \
    git checkout stash -- .env nginx/static/index.html && \
    git stash drop

then rebuild the containers:

docker compose build --no-cache && \
    docker compose up -d

Uninstall

You can revoke the generated certificates if you don't need them anymore: (replace revoke with delete to only remove the certificates from the disk)

docker run --rm -it \
    -v $PWD/certbot/letsencrypt:/etc/letsencrypt \
    certbot/certbot revoke --cert-name $DOMAIN

And to remove everything:

docker compose down --volumes --rmi all

🔧 Configuration

All the configurations take place in the .env file. It's also possible to provide these parameters as environment variable which in this case they override the values in the config file.

Note
All the parameters that start with ENABLE_, are switches. Their value evaluates to true if set to anything (including the empty value) or to false if the variable is commented out or removed entirely.

Note
All the parameters that include IPV6 in their name, will be ignored whenever ENABLE_IPV6 is not enabled.

Variable Type Description
ENABLE_XRAY switch Enables the Xray-core proxy server.
ENABLE_OCSERV switch Enables the OpenConnect VPN server.
DOMAIN string The domain to use for the web server and other TLS-based services.
XRAY_SNI string The SNI value for routing traffic to the Xray-core proxy server.
XRAY_CDN_SNI string The SNI value for routing traffic to the Xray-core proxy server originated from the CDN.
OCSERV_SNI string The SNI value for routing traffic to the OpenConnect VPN server.
EMAIL string The email address for registering the Let's Encrypt TLS certificate. Certificate expiration reminders will be sent to this email address.
TLS_PORT number The TCP port for the web server and other TLS-based services.
CDN_TLS_PORT number The TCP port for the TLS-based connections to the CDN. This value should only be changed when remapping the TLS_PORT port on the CDN. (e.g. The connections on the CDN's port 443 would be forwarded to the server's port 8433)
OCSERV_DTLS_PORT number The UDP port for the OpenConnect VPN server's DTLS protocol.
ENABLE_AUTHORITATIVE_ZONE switch Enables the authoritative DNS zone for provided DOMAIN. The generated TLS certificate is a wildcard certificate when this parameter is enabled.
ENABLE_DNSSEC switch Enables the DNSSEC for the authoritative DNS zone.
DNS_CACHE_SIZE number The DNS server's cache size in MB. The value of 0, will dedicate all the available memory.
DNS_IPV4 string The IPv4 address for forwarding the DNS queries.
DNS_IPV6 string The IPv6 address for forwarding the DNS queries.
PUBLIC_IPV4 string The Docker host public IPv4 address. The provided DOMAIN will be resolved to this IPv4 address whenever ENABLE_AUTHORITATIVE_ZONE is enabled.
OCSERV_IPV4_SUBNET string The OpenConnect VPN server's IPv4 network address.
ENABLE_IPV6 switch Enables the IPv6 for the containers.
IPV6_PREFIX string The Docker host Global Unicast IPv6 address prefix. This parameter can be used as a shorthand for other IPv6 parameters.
IPV6_SUBNET string The IPv6 network address for the containers. Network size should not be smaller than /125.
BYPASSHUB_IPV6 string The IPv6 address to allocate to the BypassHub container. This address must be in IPV6_SUBNET range.
BIND_IPV6 string The IPv6 address to allocate to the BIND container. This address must be in IPV6_SUBNET range.
CERTBOT_IPV6 string The IPv6 address to allocate to the Certbot container. This address must be in IPV6_SUBNET range.
NGINX_IPV6 string The IPv6 address to allocate to the NGINX container. This address must be in IPV6_SUBNET range. The provided DOMAIN will be resolved to this IPv6 address whenever ENABLE_AUTHORITATIVE_ZONE is enabled.
XRAY_IPV6 string The IPv6 address to allocate to the Xray-core container. This address must be in IPV6_SUBNET range.
OCSERV_IPV6 string The IPv6 address to allocate to the OpenConnect container. This address must be in IPV6_SUBNET range.
OCSERV_IPV6_SUBNET string The OpenConnect VPN server's IPv6 network address. This address must be in IPV6_SUBNET range.
OCSERV_CLIENTS_IPV6_CIDR number The IPv6 network size that will be provided to the OpenConnect VPN server clients.
ENABLE_CERTBOT_HTTP_MODE switch Enables the Certbot's standalone mode for generating the TLS certificate. The generated certificate is not a wildcard certificate and when the SNI values change, a new certificate will be generated as well and replace the old one.
ENABLE_XRAY_CDN switch Enables the Xray-core proxy server to work behind the CDN.
ENABLE_XRAY_SUBSCRIPTION switch Enables the Xray-core clients to access the configs by a subscription URL. Only authorized users have access to the subscription by providing their credentials.
NGINX_LOG_PURGE_INTERVAL number The interval in seconds that NGINX logs would be cleared. The value of 0, keeps the logs forever.
CERTBOT_RENEWAL_LEFT_DAYS number The remained days until the TLS certificate expiration. The generated TLS certificate is valid for 90 days and it will renew automatically after the remained days to the expiration date specified by this parameter have crossed. The value of 0, prevents the certificate regeneration when near to expiry or when the certificate has already expired.
CLOUDFLARE_API_TOKEN string The Cloudflare API token. This value will be used to manage the TXT DNS records on the Cloudflare DNS server during the TLS certificate generation. The generated certificate is a wildcard certificate when this parameter is specified.
OCSERV_KEY string The optional secret key for masquerading the OpenConnect VPN server identity.
ENABLE_API switch Enables the user management API.
ENABLE_API_UI switch Enables the web-based UI for interacting with the API.
API_KEY string The secret key for authenticating the API requests.

About

An abstraction around the set of tools to bypass internet censorship in extremely restricted regions.

Topics

Resources

License

Stars

Watchers

Forks