Antizapret created to redirect only blocked domains to VPN tunnel. Its called split tunneling. This repo is based on idea from original AntiZapret LXD image
- Support and discussions group
- Features
- How it works
- Installation
- Documentation
- Credits
https://t.me/antizapret_support
- Modular design. External and high quality opensource modules/containers are used as builing blocks of our system.
- User friendly web panels for administration of VPN's and DNS.
- Multiple VPN transports: Wireguard, Amnezia Wireguard, OpenVPN
- AdguardHome as main DNS resolver and blocked domains manager
- Multi-Server Architecture to bypass services geo restrictions. Different domains use different servers as exit nodes.
- Firewall to protect from port scanning
- Support for kernel modules for OpenVPN and Amnezia Wireguard to decrease CPU usage.
- SOCKS5 proxy (Dante) for per-application routing through local or world exit nodes
- List of blocked domains downloaded from open registry.
- List parsed and rules for dns resolver (adguardhome) created.
- Adguardhome resend requests for blocked domains to python script dnsmap.py.
- Python script: a) resolve real address for domain b) create fake address from 14.16.0.0/14 subnet c) create iptables rule to forward all packets from fake ip to real ip.
- Fake IP is sent in DNS response to client
- VPN tunnels configured with split tunneling. Only traffic to 14.16.0.0/14 subnet is routed through VPN.
Recommended to use server located in western countries. Some sites will block users from other countries.
- Install Docker Engine:
curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh
- Clone repository and start container:
git clone https://github.com/xtrime-ru/antizapret-vpn-docker.git antizapret cd antizapret git checkout v6 - Create docker-compose.override.yml with services you need. Minimal example with only wireguard:
services:
adguard:
environment:
- ADGUARDHOME_PASSWORD=somestrongpassword
wireguard:
environment:
- WIREGUARD_PASSWORD=somestrongpassword
extends:
file: services/wireguard/docker-compose.yml
service: wireguardFind full example in docker-compose.override.sample.yml
- Start services:
docker compose up -d
docker system prune -fVersion 5 and 6 comes with ability to forward traffic to different exit nodes for different domains. For example, YouTube works best if exit node is close to client and other services require foreign IP to work. Docker swarm is used to build unified network between containers.
Its recommended to use local server as manager/primary node for VPN's, DNS and az-local containers. Foreign server - as secondary/worker node for az-world container.
Most of the domains will be proxied through local server for maximum speed and performance. Some of the sites, which use geoip to block users, will be proxied through foreign server.
- Repeat steps 0 and 1 from single server installation on both servers:
- Install docker
- Checkout project in same location on both servers.
- [Primary] Create docker-compose.override.yml on primary node and define which services you need. See step 2 from single server installation.
- [Primary] Change hostnames of servers to az-local and az-world for ease of use:
hostnamectl set-hostname az-local - [Secondary] Change hostnames of servers to az-local and az-world for ease of use:
hostnamectl set-hostname az-world - [Optionally] hub.docker.com can be unreachable on local hostings. Proxy can be used. See instructions: https://dockerhub.timeweb.cloud
Alternatively images can be build locally on both servers:
docker compose build - [Primary]:
docker swarm init --advertise-addr <PRIMARY_SERVER_PUBLIC_IP_ADDRESS> - [Secondary]: Copy command from results and run it on secondary node:
docker swarm join --token <TOKEN> <MANAGER_IP_ADDRESS>:<PORT> - [Primary]: Inspect swarm
docker node lsID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION 6dzagr08r8d2iidkcumjjz3q7 * az-local Ready Active Leader 29.0.1 vspy2m6w4tf7uv4ywgdnzttvr az-world Ready Active 29.0.1 - [Primary] Add labels for nodes
docker node update --label-add location=local az-local && docker node update --label-add location=world az-world - [Primary, Secondary]: create config folders on both nodes:
docker compose pull; docker compose up -d; sleep 60; docker compose down; - [Primary]: start swarm
docker compose config | docker run --rm -i xtrime/antizapret-vpn:6 compose2swarm | docker stack deploy --prune -c - antizapret
- Make sure Secure DNS is disabled in your browser settings. In chrome: Navigate to Settings > Privacy and security > Security, scroll to the "Advanced" section, and toggle off "Use secure DNS"
- Install DKMS modules for openvpn and/or amnezia wireguard (if you use them):
By default, all container can be accessed via https. For certificated management separate https container is used.
If you did not provide domain and email in its env it will generate self-signed certificates
- dashboard: https://:443
- adguard: https://:1443
- filebrowser: https://:2443
- openvpn: https://:3443
- wireguard: https://:4443
- wireguard-amnezia: https://:5443
When you connected to VPN, you can access containers without exposing ports to internet:
- http://adguard.antizapret:3000
- http://dashboard.antizapret:80
- http://wireguard-amnezia.antizapret:51821
- http://wireguard.antizapret:51821
- http://openvpn-ui.antizapret:8080
- http://filebrowser.antizapret:80
By default, containers don't expose web panels to internet. All web panels are proxied via https container.
If you want to expose http to internet, add port forwarding to docker-compose.override.yml.
Example:
services:
adguard:
#...
ports:
- "3000:3000/tcp"List of default ports:
- adguard: http://:3000
- dashboard: http://:80
- wireguard-amnezia: http://:51821
- wireguard: http://:51821
- openvpn-ui: http://:8080
- filebrowser: http://:80
Some containers have same ports. So you need to choose unique external port in docker-compose.override.yml.
- Single instance
git pull --rebase docker compose down --remove-orphans docker compose up -d --remove-orphans docker system prune -af
- Swarm mode:
git pull --rebase docker pull xtrime/antizapret-vpn:6 docker compose config | docker run --rm -i xtrime/antizapret-vpn:6 compose2swarm | docker stack deploy --prune -c - antizapret docker system prune -af
- Upgrade containers:
- Docker Compose mode (single server):
docker compose down --remove-orphans git fetch && git checkout v6 && git pull --rebase docker compose down --remove-orphans docker compose up -d --remove-orphans docker system prune -af
- Swarm mode:
docker stack rm antizapret && sleep 10 git fetch && git checkout v6 && git pull --rebase docker pull xtrime/antizapret-vpn:6 docker compose config | docker run --rm -i xtrime/antizapret-vpn:6 compose2swarm | docker stack deploy --prune -c - antizapret docker system prune -af
- Update clients:
- Wireguard/Amnezia - need to download new client configs, or add
14.16.0.0/14to AllowedIps manually in old configs. - OpenVPN - need to click save at openvpn-ui server config page: http://openvpn-ui.antizapret:8080/ov/config/ and then restart openvpn server.
- Wireguard/Amnezia - need to download new client configs, or add
Remove all settings, vpn configs and return initial state of service:
docker stack rm antizapret || docker compose down --remove-orphans
rm -rf config/*- DNS Request arrives into AdGuardHome
- Adguard check it with blacklist rules. If domain in blacklist - return 0.0.0.0 and client not able to access domain.
- Adguard Send DNS request to CoreDNS service.
- CoreDNS Send DNS request to internal dnsmap.py server (antizapret container) and dnsmap.py sends request back to adguard
- Adguard receives requests one more time, but now applies rules with
$client=az-localand real upstream server client (8.8.8.8 by default) - If domain in whitelist - adguard will resolve its address and return to dnsmap.py
- If domain not in whitelist adguard return SERVFAIL
- dnsmap.py send response to adguard:
- If it is valid IP, then replaces it with "internal" IP from
14.16.0.0/15subnet, add masquerade to iptables and return internal ip to adguard - If is is SERVFAIL it sends this response to client.
- If it is valid IP, then replaces it with "internal" IP from
- If CoreDNS receives SERVFAIL it retries request and send it directly to Adguard. In this case rules with
$client=az-localdo not applied and request processed normally.
Why so complicated?
- Windows and some other clients do not retry to Fallback DNS, even if SERVFAIL received. So we added CoreDNS for that.
- Adguard don't allow to redefine upstream in blacklist/whitelist rules. But this rules have regex support and updated automatically, so we want to use them. So multiple requests from different clients are made internally.
- Adguard allows different upstreams for different clients. So we can use different DNS for blocked and non blocked domains.
There are two ways of adding domains. Via custom rules and via black lists.
Open adguard panel: http://adguard.antizapret:3000/#custom_rules Rules/syntaxes: https://adguard-dns.io/kb/general/dns-filtering-syntax/#basic-examples
By default, adguard rewrite all requests with SERVFAIL. This is a trick to make client retry DNS request to second, local DNS server.
Rules with the dnsrewrite response modifier have higher priority than other rules in AdGuard Home and AdGuard DNS.
To override default rule custom rules must have $dnsrewrite modifier.
To support default adguard filters default SERVFAIL rule applied only to internal requests from client=az-local and client=az-world
Examples:
@@||subdomain.host.com^$dnsrewrite,client=az-local
@@||*.host.com^$dnsrewrite,client=az-local
@@||host.com^$dnsrewrite,client=az-world
@@||de^$dnsrewrite,client=az-world
@@/some_.*_regex/$dnsrewrite,client=az-local
Also you can add any urls to blocklist. http://adguard.antizapret:3000/#dns_blocklist Need to use adapter, to parse and adapt list in different formats.
- Add domains for local exit node:
http://az-local.antizapret/list/?url=<ANY_URL> - Add domains for world exit node
http://az-world.antizapret/list/?url=<ANY_URL>Supported formats: simple list of domains, adguard format, hosts format, json array of domains, regex list.
Options for adapter:
url- download list from urlfile- read local file. Used for include-host-{custom,dist}.txtfilter_custom=1- filter lists with rules from exclude-hosts-custom.txt.filter_dist=0- filter lists with rules from exclude-hosts-dist.txtformat=list- 'list' or 'json'. Detected automatically.client=az-local- name of client to add to rules. Detected automatically.allow=1- disable this option, to block domains from list for this exit node.raw=0- dont modify rulessuffix=1- add "$dnsrewrite,client=xxx" to rules
Add ips and subnets to ./config/antizapret/custom/include-ips-custom.txt.
Containers periodically check changes in config folder (every 5-10 seconds) and restart/update after any change.
Trigger update manually: docker exec $(docker ps -q --filter=name=az | head -n1) doall
AntiZapret uses DNS-based split tunneling, which works only for domain-based connections. If an application connects directly by IP address, DNS interception does not work and traffic is not routed through the VPN tunnel.
Adding large number of IPs to include-ips-custom.txt can cause issues with OpenVPN (push routes limit), so Dante SOCKS5 proxy was added as an alternative solution.
- Connect to VPN (OpenVPN, WireGuard or Amnezia WireGuard)
- Configure your application to use SOCKS5 proxy via tools like AntizapretSOCKS5 (Windows), ProxyBridge, Proxifier, or browser proxy settings
- All traffic from that application (including direct IP connections) will exit through the selected server node
Two socks5 proxy containers are available:
socks-local.antizapret:8118— traffic exits through the local serversocks-world.antizapret:8118— traffic exits through the world server
Authentication: SOCKS5 with username/password (configured via environment variables).
To disable authentication, omit SOCKS_USERNAME and SOCKS_PASSWORD (or leave them empty).
| Scenario | DNS routing | Dante SOCKS5 |
|---|---|---|
| Application connects by domain | ✅ Works | ✅ Works |
| Application connects by IP | ❌ Not routed | ✅ Works |
| Large number of IPs to route | ❌ OpenVPN push routes limit | ✅ No limit |
| Per-application exit node selection | ❌ | ✅ Choose local or world per app |
Add socks5 services to docker-compose.override.yml:
socks-local:
hostname: socks-local.antizapret
extends:
file: services/socks/compose.yml
service: socks
environment:
- SOCKS_USERNAME=admin
- SOCKS_PASSWORD=password
deploy:
mode: replicated
replicas: 1
endpoint_mode: dnsrr
placement:
constraints: [ node.labels.location == local ]
socks-world:
hostname: socks-world.antizapret
extends:
file: services/socks/compose.yml
service: socks
environment:
- SOCKS_USERNAME=admin
- SOCKS_PASSWORD=password
deploy:
mode: replicated
replicas: 1
endpoint_mode: dnsrr
placement:
constraints: [ node.labels.location == world ]Note:
socks-worldrequires Docker Swarm mode with two nodes. On a single server onlysocks-localwill work.
- Connect to VPN
- Configure SOCKS5 proxy in your application or proxy manager:
- Host:
socks-local.antizapretorsocks-world.antizapret - Port:
8118 - Type: SOCKS5
- Username: value of
SOCKS_USERNAME - Password: value of
SOCKS_PASSWORD
- Host:
For Windows clients, use AntizapretSOCKS5 — a GUI application for configuring per-application SOCKS5 routing using ProxiFyre.
- Download and extract AntizapretSOCKS5
- Run
ConfigEditor.exeand install the Windows Packet Filter driver when prompted - Add proxy configurations — select applications, choose proxy server (
socks-local.antizapret:8118orsocks-world.antizapret:8118), and set credentials - Save configuration and start ProxiFyre
- Game client that connects to servers by IP — route through
socks-worldto bypass geo-restrictions - Torrent client — route through
socks-worldfor foreign IP - Browser — use proxy extension to route specific sites through
socks-localorsocks-world - Application with many hardcoded IPs — instead of adding hundreds of IPs to
include-ips-custom.txt, just proxy the whole app through socks5
You can define these variables in docker-compose.override.yml file for your needs:
Antizapret: Consists of two containers: az-local and az-world. This is VPN exit nodes.
DNS=adguard- Upstream DNS for resolving blocked sites (adguard by default)AZ_SUBNET=14.16.0.0/14Subnet for virtual addresses for blocked hosts.ROUTES- list of VPN containers and their virtual addresses. Used for iperf3 server.DOALL_DISABLED=- skip run on az-world node.
Adguard:
ROUTES- list of VPN containers and their virtual addresses. Used for unique client addresses in adguard logsADGUARDHOME_PORT=3000ADGUARDHOME_USERNAME=adminADGUARDHOME_PASSWORD=ADGUARDHOME_PASSWORD_HASH=- hashed password, taken from the AdGuardHome.yaml file after the first run usingADGUARDHOME_PASSWORD. Dollar sign$in hash must be escaped with another dollar sign:$$
CoreDNS:
- None
Filebrowser:
FILEBROWSER_PORT=adminFILEBROWSER_PASSWORD=password
Proxy:
PROXY_DOMAIN=- create letsencrypt https certificate for domain. If not set host ip is used for self-signed certificate.PROXY_EMAIL=- email for letsecnrypt certificate.SOCKS_EXTERNAL_IFACES- comma-separated list of external network interfaces for the SOCKS proxy (e.g.eth0,eth1). If omitted, interfaces are auto-detected; falls back toeth0when none are found
Openvpn
ROUTESOBFUSCATE_TYPE=0- custom obfuscation level of openvpn protocol. 0 - disable.Act as regular openvpn client, support by all clients. 1 - light obfuscation, works with microtics 2 - strong obfuscation, works with some clients: openvpn gui client, asuswrt client...AZ_SUBNET=14.16.0.0/14- subnet for virtual blocked ips.
Openvpn-ui
OPENVPN_ADMIN_USERNAME=- replace default username with your usernameOPENVPN_ADMIN_PASSWORD=- replace default password with your passwordOPENVPN_EXTERNAL_IP- external ip of your server, by default detected automaticallyOPENVPN_DNS=14.16.0.1- DNS address for clients. Must be inANTIZAPRET_SUBNETOPENVPN_LOCAL_IP_RANGE=10.1.165.0- subnet for ovpn clients. Subnet can be viewed in adguard journal or in ovpn-ui panel
Wireguard/Wireguard Amnezia
ROUTESWIREGUARD_PASSWORD=- password for admin panelWIREGUARD_PASSWORD_HASH=- hashed password for admin panelAZ_SUBNET=14.16.0.0/14- subnet for virtual blocked ips.WG_DEFAULT_DNS=14.16.0.1- DNS address for clients. Must be inANTIZAPRET_SUBNETWG_PERSISTENT_KEEPALIVE=25PORT=51821- admin panel portWG_PORT=51820- wireguard server portWG_DEVICE=eth0
Dante SOCKS5 Proxy
SOCKS_USERNAME- username for SOCKS5 authentication (omit to disable authentication)SOCKS_PASSWORD- password for SOCKS5 authentication (omit to disable authentication)
Adguard uses Google DNS and Quad9 DNS to resolve unblocked domains. This upstreams support ECS requests (more info below). Cloudflare DNS do not support ECS and is not recommended for use.
Source code: Adguard upstream DNS
After container is started working copy is located here: ./config/adguard/conf/upstream_dns_file_basis
Some domains can resolve differently, depending on subnet (geoip) of client. In this case using of DNS located on remote server will break some services. ECS allow to provide client IP in DNS requests to upstream server and get correct results. Its enabled by default in Adguard and client ip is pointed to Moscow (Yandex Subnet).
If you located in other region, you need to replace 77.88.8.8 with your real ip address on this page http://your-server-ip:3000/#dns
https://github.com/d3vilh/openvpn-ui?tab=readme-ov-file#generating-ovpn-client-profiles
- go to
http://%your_ip%:8080/certificates - click "create certificate"
- enter unique name. Leave all other fields empty
- click create
- click on certificate name in list to download ovpn file.
OpenVPN Data Channel Offload (DCO) provides performance improvements by moving the data channel handling to the kernel space, where it can be handled more efficiently and with multi-threading. tl;dr it increases speed and reduces CPU usage on a server.
Kernel extensions can be installed only on a host machine, not in a container.
sudo apt install -y openvpn-dco-dkmssudo apt update
sudo apt upgrade
echo "#### Please reboot your system after upgrade ###" && sleep 100
deb=openvpn-dco-dkms_0.0+git20231103-1_all.deb
sudo apt install -y efivar dkms linux-headers-$(uname -r)
wget http://archive.ubuntu.com/ubuntu/pool/universe/o/openvpn-dco-dkms/$deb
sudo dpkg -i $debIf your clients do not have GCM ciphers support you can use legacy CBC ciphers. DCO is incompatible with legacy ciphers and will be disabled. This is also increase CPU load.
https://github.com/amnezia-vpn/amneziawg-linux-kernel-module?tab=readme-ov-file#ubuntu
sudo add-apt-repository ppa:amnezia/ppasudo apt install -y amneziawg- restart server or
docker compose restart wireguard-amnezia - check the list of kernel modules
dkms status, and check that bunch of[kworker/X:X-wg-crypt-wg0]processes are now running.
- Edit
etc/apt/sources.listand uncommentdeb-src http://archive.ubuntu.com/ubuntu ... main restricted sudo apt updatesudo apt install -y software-properties-common python3-launchpadlib gnupg2 linux-headers-$(uname -r)- install source for kernel
sudo apt-get source linux-image-$(uname -r) sudo add-apt-repository ppa:amnezia/ppasudo apt install -y amneziawgsudo dkms install -m amneziawg -v 1.0.0- restart server or
docker compose restart wireguard-amnezia - check the list of kernel modules
dkms status, and check that bunch of[kworker/X:X-wg-crypt-wg0]processes are now running.
Amnezia adds random packets to change signature of wireguard protocol and bypass DPI.
By default we use JMIN=20; JMAX=100 for junk packet size in bytes.
Large junk packets can help to bypass DPI, but some firewalls can block them as DDOS attack. Use env variables to change their size if you have issues with amnezia connection:
Jc=3
Jmin=20
Jmax=100
or
Jc=2
Jmin=10
Jmax=20
Example part of docker-compose.override.yml with JMIN and JMAX:
wireguard-amnezia:
environment:
- WIREGUARD_PASSWORD=xxxxx
- JC=2
- JMIN=10
- JMAX=20
extends:
file: services/wireguard/docker-compose.yml
service: wireguard-amneziaSettings/env variables are saved in ./config/wireguard_amnezia/ folder. To update them remove folder and run container again. This will also remove all existing clients/certificates.
docker compose down && rm -rf ./config/wireguard_amnezia/ && docker compose up -dMost providers now block vpn to foreign IPs. Obfuscation in amnezia or openvpn not always fix the issue. For stable vpn operation you can try to connect to VPS inside of your country and then proxy traffic to foreign server.
There are two ways:
- [Recommended] Install in docker swarm mode
- Proxy all traffic via local proxy. See below.
Example of startup script. Replace <SERVER_IP> with IP address of your server and run it on fresh VPS (ubuntu 24.04 is recommended):
#!/bin/sh
# Fill with your foreign server ip
export VPN_IP=<SERVER_IP>
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.d/99-sysctl.conf
sysctl -w net.ipv4.ip_forward=1
# DNAT rules
iptables -t nat -A PREROUTING -p tcp ! --dport 22 -j DNAT --to-destination "$VPN_IP"
iptables -t nat -A PREROUTING -p udp ! --dport 22 -j DNAT --to-destination "$VPN_IP"
# MASQUERADE rules
iptables -t nat -A POSTROUTING -p tcp -d "$VPN_IP" -j MASQUERADE
iptables -t nat -A POSTROUTING -p udp -d "$VPN_IP" -j MASQUERADE
echo iptables-persistent iptables-persistent/autosave_v4 boolean true | sudo debconf-set-selections
echo iptables-persistent iptables-persistent/autosave_v6 boolean false | sudo debconf-set-selections
apt install -y iptables-persistent
- OpenWrt setup guide - how to setup OpenWrt router with this solution to keep LAN clients happy.
- Keenetic setup guide - instructions for configuring the server and connecting Keenetic routers to it (на русском языке)
iperf3 server is included in antizapret-vpn container.
- Connect to VPN
- Use iperf3 client on your phone or computer to check upload/download speed.
Example 10 threads for 10 seconds and report result every second:
# local node iperf3 -c az-local.antizapret -i1 -t10 -P10 iperf3 -c az-local.antizapret -i1 -t10 -P10 -R # world node iperf3 -c az-world.antizapret -i1 -t10 -P10 iperf3 -c az-world.antizapret -i1 -t10 -P10 -R
- ProstoVPN — the original project
- AntiZapret VPN Container — source code of the LXD-based container
- AntiZapret PAC Generator — proxy auto-configuration generator to bypass censorship of Russian Federation
- Amnezia WireGuard VPN — used for Amnezia Wireguard integration
- WireGuard VPN — used for Wireguard integration
- OpenVPN - used for OpenVPN integration
- AdGuardHome - DNS resolver
- filebrowser - web file browser & editor
- lighttpd - web server for unified dashboard
- caddy - reverse proxy
- No Thought Is a Crime — a forum about technical, political and economical aspects of internet censorship in different countries
- Dante - SOCKS5 proxy server for per-application routing
