If you arrived at this page via one of the links to AntiZapret, don’t be surprised — this used to be it =)
I liked the idea of targeted routing implemented in this container, but the container itself became outdated N years ago.
I used it as the basis for my own container, ambitiously named nspawn-router, rewrote it with an up-to-date stack, added the functionality I needed, and removed AntiZapret.
Now this container can be used to organize routing for your own purposes. The core still relies on the combination of
OpenVPN + Knot Resolver + nftables + dnsmap
.Read more about VPN technology and its connection to restricted services.
The container author is not responsible for how or by whom this container will be used!
nspawn-router - a containerized solution for organizing traffic routing. It allows you to isolate network flows, direct them through specified interfaces or tunnels, and use the container as an intermediate router/filter without involving the host system.
The container uses domain name–based routing via a dedicated DNS server created for this purpose.
The DNS resolver maps the real IP address of a domain to a free IP address within a large internal subnet and returns the internal subnet address to the requesting client.
This approach has several advantages:
But there are also downsides:
Schematic representation:
📱 — Client
🖥 — A container with a running DNS server
🖧 — Internet
📱 → chatgpt.com? → 🖥
🖥 → chatgpt.com? → 🖧
🖥 ← 172.64.155.209 ← 🖧
10.224.0.1 → 172.64.155.209
📱 ← 10.224.0.1 ← 🖥
The current version of the special DNS server does not support IPv6 and deliberately removes IPv6 addresses (AAAA records) from DNS responses.
This is not a significant drawback, as websites accessible only via IPv6 and not IPv4 are practically nonexistent.
Log in to the system using SSH:
the hosting provider usually provides the server’s IP address, login, and password.
All commands should be executed as the root
user or with sudo
.
# Installing the container with default parameters
# The OpenVPN configuration file needs to be imported into the OpenVPN Connect application
wget -qO- https://kyzima-spb.github.io/router/installer.sh | \
sudo bash -s -- --cn client > client.ovpn
# You can set a password, in which case the configuration will be saved in a ZIP archive
wget -qO- https://kyzima-spb.github.io/router/installer.sh | \
sudo bash -s -- --cn client --password 'very secret' > client.zip
# You can specify your own name for the container
wget -qO- https://kyzima-spb.github.io/router/installer.sh | \
sudo bash -s -- -n custom-router --cn client > client.ovpn
# You can specify a domain or a fixed port
wget -qO- https://kyzima-spb.github.io/router/installer.sh | \
sudo bash -s -- --cn client --remote example.com -p 1194 > client.ovpn
# Or, if you need to use the TCP protocol
wget -qO- https://kyzima-spb.github.io/router/installer.sh | \
sudo bash -s -- --cn client --proto tcp > client.ovpn
# For more details, see the help for the install command
wget -qO- https://kyzima-spb.github.io/router/installer.sh | \
sudo bash -s -- install -h
All configuration files, certificates, and keys can be copied from the server to your computer using FileZilla (Windows, macOS, Linux) or WinSCP (Windows only) over the SFTP protocol.
If you haven’t changed the directory after logging in, by default it is the user’s home directory.
For the root user, this is /root
; for other users, it is /home/<USERNAME>
.
On Linux, you can archive all the files and download them from the server with the command:
whoami # root
pwd # /root
tar -czf /root/credentials.tar.gz <PATH_1> <PATH_2> <PATH_N>
scp <USER>@<PUBLIC_IP>:/root/credentials.tar.gz <DEST_PATH>
apt update && apt install -y gnupg systemd-container
gpg -k > /dev/null
gpg \
--no-default-keyring \
--keyring /etc/systemd/import-pubring.gpg \
--keyserver hkps://keyserver.ubuntu.com \
--receive-keys 0xA2AFF7EB363E6C8DD27655AD62CD962F89DDC0CD
machinectl pull-tar https://github.com/kyzima-spb/router/releases/download/v1.0/router.tar.xz
mkdir -p /etc/systemd/nspawn
tee /etc/systemd/nspawn/router.nspawn <<- EOF
[Exec]
NotifyReady=yes
PrivateUsers=yes
[Network]
VirtualEthernet=yes
Port=tcp:1194:1194
Port=udp:1194:1194
EOF
systemctl enable --now systemd-networkd.service
machinectl enable router
machinectl start router
# Wait for the VPN server configuration to be created
systemd-run -q -M router --wait --pipe journalctl -u openvpn-generate-keys -f | \
grep -q 'Deactivated successfully.'
To remove the container, the image, and all related files, run:
wget -qO- https://kyzima-spb.github.io/router/installer.sh | \
sudo bash -s -- uninstall
If a different name was specified during installation, run:
wget -qO- https://kyzima-spb.github.io/router/installer.sh | \
sudo bash -s -- uninstall -n custom-router
Selecting the right VPS starts with two key factors: the server’s physical location and its latency (ping). These have the biggest impact on performance and user experience.
As for system requirements, only a few basics really matter. Below you’ll find the minimum specs you should pay attention to - everything else plays only a minor role and won’t significantly affect your setup.
systemd-machined
available (Debian 12/13 recommended)All links are referral links!!!
Client management for OpenVPN inside the container is handled by the client-util
script.
For more details, see the help documentation. Here are a few examples for common use cases.
Commands can be executed inside the container:
# To login the container, use the command:
machinectl shell <CONTAINER_NAME>
# Inside the container, view the help for the command:
client-util -h
Or from the host:
systemd-run -q --wait --pipe -M <CONTAINER_NAME> client-util -h
To add a new client named phone
, run the command:
systemd-run -q --wait --pipe -M <CONTAINER_NAME> client-util generate phone
To recreate the certificate and key for an existing client, use the --revoke
argument.
This will revoke the previously issued certificate and key:
systemd-run -q --wait --pipe -M <CONTAINER_NAME> client-util generate phone --revoke
Deleting a client is not supported, but you can revoke the certificate and key issued to them. To do this, run the command:
systemd-run -q --wait --pipe -M <CONTAINER_NAME> client-util revoke phone
To generate a configuration file for the OpenVPN Connect application, run the command:
systemd-run -q --wait --pipe -M <CONTAINER_NAME> client-util show phone > phone.ovpn
If the port was not explicitly specified during installation, a random available port will be used. Inside the container, this port is unknown, so when generating OVPN files, you need to explicitly specify the port:
VPN_PORT="$(grep '^Port=' /etc/systemd/nspawn/<CONTAINER_NAME>.nspawn | awk -F: '{print $2}' | head -1)"
systemd-run -q --wait --pipe -M <CONTAINER_NAME> client-util show phone -p "$VPN_PORT" > phone.ovpn
For more details, see the help for the client-util:
systemd-run -q --wait --pipe -M <CONTAINER_NAME> client-util -h
Create a configuration file with the .conf
extension in the /etc/knot-resolver/kresd.conf.d
directory.
A number and a hyphen at the beginning of the filename indicate its priority.
Add the domains or zone you need to the file, then restart Knot Resolver:
tee /etc/knot-resolver/kresd.conf.d/10-ai.conf <<- EOF
local ai_domains = policy.todnames({
'chatgpt.com.', 'openai.com.', 'recraft.ai.'
})
policy.add(
policy.suffix(
policy.STUB({'127.0.0.4'}),
ai_domains
)
)
EOF
systemctl restart kresd@1
To verify inside the container, run the dig
command for a domain from the list.
You should see an IP address from the internal network:
machinectl shell <CONTAINER_NAME>
dig @127.0.0.1 chatgpt.com # 10.224.0.N
Another example: adding alternative domain zones from the OpenNIC project (DNS server IP addresses are provided for illustration purposes):
tee /etc/knot-resolver/kresd.conf.d/05-opennic.conf <<- EOF
local opennic_dns = {
'94.247.43.254',
'194.36.144.87',
'217.160.70.42',
}
local opennic_domains = policy.todnames({
'bbs.', 'chan.', 'cyb.', 'dyn.', 'geek.', 'gopher.',
'indy.', 'libre.', 'neo.', 'null.', 'o.', 'oss.', 'oz.',
'parody.', 'pirate.', 'free.', 'bazar.', 'coin.',
'emc.', 'lib.', 'fur.', 'bit.', 'ku.', 'te.', 'ti.', 'uu.',
})
policy.add(
policy.suffix(policy.STUB(opennic_dns), opennic_domains)
)
EOF
systemctl restart kresd@1
machinectl shell <CONTAINER_NAME>
dig @127.0.0.1 grep.geek # 161.97.219.84
If there is a web server within your local network, you can create your own local domain:
tee /etc/knot-resolver/kresd.conf.d/01-zone-loc.conf <<- EOF
policy.add(
policy.suffix(
policy.ANSWER(
{ [kres.type.A] = { rdata=kres.str2ip('192.168.88.10'), ttl=300 } }
),
{ todname('loc.') }
)
)
EOF
systemctl restart kresd@1
machinectl shell <CONTAINER_NAME>
dig @127.0.0.1 site.loc # 192.168.88.10
dig @127.0.0.1 test.loc # 192.168.88.10
Since March 2022, %username%
learned a new word and, as is typical, gave it a new meaning -
access to all the benefits of humanity. But that’s not what it actually means!
VPN (Virtual Private Network) is a technology that provides a secure connection over the Internet between different networks or devices, creating a single virtual network on top of the public one.
For example, the headquarters has its own local subnet. Branches also have their own local subnets, which may be located in the same city as the headquarters or in completely different cities. The headquarters has no access to the branch subnets, and the branches have no access to each other’s subnets.
To establish such access, you need a machine that is “visible” to every subnet. A VPN server is configured on this machine, and all subnets connect to it. Now each subnet becomes part of a virtual network, allowing devices to interact directly with each other as if they were on the same local network.
Thus, a VPN solves the key tasks: subnet unification, data encryption, tunneling, and authentication. All other benefits are merely side effects of this technology.