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.
____________
| 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.
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 buildFill the following parameters in the config file with your information:
Note
It's also recommended to changeXRAY_SNI,XRAY_CDN_SNIandOCSERV_SNIsubdomain part to something else. For example, defaultXRAY_SNIvalue isxr.$DOMAIN, you can change it tohotdog.$DOMAINor 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 theCLOUDFLARE_API_TOKENparameter. 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.
BothENABLE_AUTHORITATIVE_ZONEandENABLE_CERTBOT_HTTP_MODEparameters should be disabled for this to take effect.
Warning
You need to stop any service that you might have listening on the TCP port80if theENABLE_CERTBOT_HTTP_MODEparameter 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 -dSee the user management page.
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=PASSWORDAdding 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: TLSor 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_SNIUsing IP Address Instead of Domain
If your domain already is blocked, change
XRAY_SNIparameter'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: (replacePUBLIC_IPV4withNGINX_IPV6if you want to use IPv6)Address: $PUBLIC_IPV4 SNI: $XRAY_SNIYou 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-dtlsargument for a faster initial connection.Note
IfOCSERV_KEYparameter is set,--serverargument 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_SNIparameter'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: (replacePUBLIC_IPV4withNGINX_IPV6if you want to use IPv6)--sni $OCSERV_SNI ^ --resolve $OCSERV_SNI:$PUBLIC_IPV4After reconnecting with added arguments, you also need to add the printed fingerprint token as the
--servercertargument:--servercert pin-sha256:Q8CaEEFJqy2xyD9+SsAwfMuVH7jz/Rq0r/HXmNkIg9k=
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/orAAAA) record forDOMAIN,XRAY_SNI,XRAY_CDN_SNI,OCSERV_SNIandwww.$DOMAINand point them to thePUBLIC_IPV4(orNGINX_IPV6). The Proxy status should be enabled for all except for theXRAY_SNIandOCSERV_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_SUBSCRIPTIONparameter is enabled, you should set the Caching Level option to the No query string in the Configuration section of Caching panel. - The
ENABLE_AUTHORITATIVE_ZONEandENABLE_XRAY_CDNparameters should be disabled and enabled respectively. Rebuild the containers after the modification. - Update your
Xray-coreclient 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.data/xray/configs/cdn-ipsto 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_SNIandOCSERV_SNIto hide your server's IP address from the national firewall.
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 .data/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
OpenConnectVPN server can be detected through the exposed SNI. It's also recommended to setOCSERV_KEYparameter to hide the server identity.
For Xray-core you can place additional configuration files in the .data/xray/configs directory (e.g. to block BitTorrent traffic).
For OpenConnect, you can place user-based configuration files in the .data/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.
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-depsand 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"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 ACCEPTThe 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 /var/log/nginx/access.log # Clients access log
docker compose exec nginx cat /var/log/nginx/static.log # Dummy website access log
docker compose exec nginx cat /var/log/nginx/api.log # API access log
docker compose exec nginx cat /var/log/nginx/error.logStop and remove the current containers:
docker compose down --rmi allGet the latest source:
git stash && \
git pull && \
git checkout stash -- .env && \
git stash dropthen rebuild the containers:
docker compose build --no-cache && \
docker compose up -dYou 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/.data/certbot/letsencrypt:/etc/letsencrypt \
certbot/certbot revoke --cert-name $DOMAINAnd to remove everything:
docker compose down --volumes --rmi allAll 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 withENABLE_, are switches. Their value evaluates totrueif set to anything (including the empty value) or tofalseif the variable is commented out or removed entirely.
Note
All the parameters that includeIPV6in their name, will be ignored wheneverENABLE_IPV6is 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. |
| 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. |
| 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. |