mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-10-03 21:40:12 +00:00
247 lines
11 KiB
Markdown
247 lines
11 KiB
Markdown
# Zapret2 {#module-services-zapret2}
|
||
|
||
[Zapret2](https://github.com/bol-van/zapret2) is a service that enables
|
||
bypassing DPI systems using extensible Lua filters that process outgoing
|
||
network traffic.
|
||
|
||
For details on which parameters are available and their usage, consult the
|
||
[upstream documentation][1].
|
||
|
||
## Quick Start {#module-services-zapret2-quick-start}
|
||
|
||
A simple, minimal setup that includes only a single profile that applies two
|
||
Lua [instances][2] to TLS ClientHello packets can be defined as follows:
|
||
|
||
```nix
|
||
{
|
||
services.zapret2 = {
|
||
enable = true;
|
||
profiles.default.parameters = [
|
||
"--filter-tcp=443"
|
||
"--payload=tls_client_hello"
|
||
"--lua-desync=fake:blob=fake_default_tls:tcp_ts=-1000:repeats=1"
|
||
"--lua-desync=fakedsplit:pos=1,midsld:tcp_ts=-1000"
|
||
];
|
||
};
|
||
}
|
||
```
|
||
|
||
## Configuration {#module-services-zapret2-configuration}
|
||
|
||
The NixOS module for Zapret2 adds a small structured interface on top of the
|
||
typical plain argument list that is passed to `nfqws2`. This allows e.g.
|
||
profiles to be defined across multiple modules and merged correctly.
|
||
|
||
### Profiles {#module-services-zapret2-configuration-profiles}
|
||
|
||
Each Zapret2 profile is defined under the {option}`services.zapret2.profiles`
|
||
option. Multiple profiles can be defined at once, however it's important to
|
||
ensure that each profile can only match one category of traffic by using
|
||
`--filter-*` parameters, because otherwise a profile will match all traffic,
|
||
and since first match wins, no other profile can match it, even if it has a
|
||
more specific filter:
|
||
|
||
```nix
|
||
{
|
||
services.zapret2.profiles = {
|
||
http.parameters = [
|
||
"--filter-tcp=80"
|
||
"--payload=http_req"
|
||
"--lua-desync=http_methodeol"
|
||
];
|
||
https.parameters = [
|
||
"--filter-tcp=443"
|
||
"--payload=tls_client_hello"
|
||
"--lua-desync=multisplit:pos=1,sniext+1,host+1,midsld-2,midsld,midsld+2,endhost-1"
|
||
];
|
||
stun.parameters = [
|
||
"--filter-udp=*"
|
||
"--payload=stun,discord_ip_discovery"
|
||
"--lua-desync=fake:blob=0x00000000000000000000000000000000:repeats=2"
|
||
];
|
||
};
|
||
}
|
||
```
|
||
|
||
Since profiles are matched on their order and first match wins, each profile
|
||
also has a {option}`services.zapret2.profiles.‹name›.priority` option. Lower
|
||
values will cause the profile to be ordered *before* others, higher values will
|
||
cause the profile to be ordered *after* others. The default priority is `1000`.
|
||
In other words, if you want to define a "fallback" profile that matches traffic
|
||
not matched by any other profile, you should set the priority to a higher value
|
||
such as `1500`.
|
||
|
||
Each profile's parameters can have the typical filters such as `--filter-tcp=*`
|
||
or `--filter-udp=*`, however for matching hostnames and IP addresses, there are
|
||
a few options to make this more convenient:
|
||
|
||
```nix
|
||
{
|
||
services.zapret2.profiles.default = {
|
||
hosts = {
|
||
# Automatically keep track of which hosts need the DPI bypass and which
|
||
# don't. This works by Zapret2 checking if the connection would otherwise
|
||
# be matched by the profile, and if it is, it first bypasses it, however
|
||
# it monitors the connection to see if it gets denied (for example, if a
|
||
# TCP RST packet is received right after the TLS ClientHello is sent). If
|
||
# this happens, it will then add it to the auto host list (that is
|
||
# persisted on disk), so subsequent connections will succeed.
|
||
autodetect.enable = true;
|
||
|
||
# If you wish to change where the automatic hostlist file is saved, from
|
||
# the default location of `/var/lib/zapret2/‹name›-hosts.txt`, for
|
||
# example to share between multiple profiles:
|
||
autodetect.file = "/var/lib/zapret2/hosts.txt";
|
||
|
||
# Hardcoded list of DNS domains to include/exclude. Note that domains are
|
||
# matched including all their subdomains, so `nixos.org` also includes
|
||
# `cache.nixos.org` for example, and `ru` includes `gosuslugi.ru`. If you
|
||
# don't want this behaviour, you should add `^` to the beginning of the
|
||
# entry, e.g. `^example.com` will match `example.com` but not
|
||
# `www.example.com`.
|
||
include = [
|
||
"cachix.org"
|
||
"nixos.org"
|
||
];
|
||
exclude = [ "ru" ];
|
||
};
|
||
|
||
ips = {
|
||
# Like with hostnames, hardcoded list of IP addresses or subnets in CIDR
|
||
# notation to include/exclude. Note that by default, the firewall already
|
||
# excludes local networks (as defined by RFC 1918), so they are not
|
||
# passed to Zapret2.
|
||
include = [ "213.59.192.0/18" ];
|
||
exclude = [ "173.245.48.0/20" ];
|
||
};
|
||
};
|
||
}
|
||
```
|
||
|
||
If either an IP/host include list is defined, or {option}`hosts.autodetect` is
|
||
enabled, the profile will enter a whitelist mode, where traffic by default
|
||
won't be matched (except the special connection tracking case of the auto
|
||
mode). If an IP/host exclude list is defined, traffic not on the exclude list
|
||
is still matched by default, unless an include list is also defined.
|
||
|
||
In addition to the options above, you may of course define `--ipset-*` or
|
||
`--hostlist-*` options in the profile's parameters. This can be useful to e.g.
|
||
maintain an externally updated list that is not hardcoded into the NixOS
|
||
configuration. If you use these options, make sure the file paths are
|
||
accessible by the user that Zapret2 runs as (by default, it runs as a dynamic
|
||
user).
|
||
|
||
### Lua Files {#module-services-zapret2-configuration-lua-files}
|
||
|
||
By default, the module loads the `zapret-lib` and `zapret-antidpi` files from
|
||
the package defined by {option}`services.zapret2.package`. The list of files to
|
||
load can be customised using the {option}`services.zapret2.files` option, as
|
||
follows:
|
||
|
||
```nix
|
||
{
|
||
services.zapret2.files = [
|
||
"zapret-lib"
|
||
"zapret-antidpi"
|
||
"zapret-obfs"
|
||
./my-lib.lua
|
||
"/path/to/read/at/runtime/my-lib.lua"
|
||
];
|
||
}
|
||
```
|
||
|
||
Each entry in the list of files can either be a path value, an absolute string
|
||
path (with the `.lua` extension), or the name of a [Zapret2 Lua library][3]
|
||
(without the `.lua` extension). For example, `zapret-lib` gets resolved to
|
||
`zapret-lib.lua` from the defined {option}`services.zapret2.package`. By
|
||
default, `zapret-lib` and `zapret-antidpi` are already included, so for the
|
||
simple case of DPI bypass using the standard libraries, configuring this option
|
||
isn't needed.
|
||
|
||
However, note that if you *do* customise this option, it overrides the default
|
||
list of files completely. So, for example, if you are writing custom Lua desync
|
||
functions, you will need to not only include your library's full path, but also
|
||
any libraries that it depends on (typically `zapret-lib`).
|
||
|
||
### Firewall {#module-services-zapret2-configuration-firewall}
|
||
|
||
Options for the generated nftables firewall configuration can be customised
|
||
under the {option}`services.zapret2.firewall` option. Since Zapret2 is a
|
||
userspace program and all network traffic originates in the kernel, all
|
||
matching network traffic has to be passed from kernel space to userspace. This
|
||
is an expensive process that can slow down network traffic if too much traffic
|
||
is processed. So, it is important to process as little traffic as possible to
|
||
maintain high network throughput. The predefined firewall already excludes
|
||
local IP ranges (as defined by RFC 1918), and the module includes a number of
|
||
options to further reduce how much traffic is passed to Zapret2:
|
||
|
||
```nix
|
||
{
|
||
services.zapret2.firewall = {
|
||
# The maximum number of packets *per each connection* that is passed to
|
||
# Zapret2, where connection is defined by conntrack. So for the default
|
||
# value of 16, it will pass the first 16 packets of the connection to
|
||
# Zapret2 for processing. After 16 packets the firewall won't pass any
|
||
# packets, and the connection will completely bypass Zapret2. This is
|
||
# sufficient for most anti-DPI mangling. However if you are doing more
|
||
# complex processing, you may have to increase this. If you are doing
|
||
# obfuscation, you will want to set this to `null` so that every packet is
|
||
# passed. Otherwise obfuscation will only apply to the first 16 packets.
|
||
maxPackets = 16;
|
||
|
||
# The interfaces on which traffic will be passed to Zapret2 for processing.
|
||
# By default this is `null`, which matches every interface including e.g.
|
||
# loopback and VPN tunnels, which don't need processing. You should almost
|
||
# always set this to your actual outbound network interface(s) to prevent
|
||
# VPN traffic on the inside of the tunnel from getting mangled.
|
||
interfaces = [ "eth0" ];
|
||
|
||
# TCP and UDP ports routed to Zapret2 at the firewall level. Any ports not
|
||
# in these lists will bypass processing completely. No profile can ever
|
||
# match ports not on these lists, since they are not even passed to
|
||
# Zapret2. By default these are both `null`, meaning connections to all
|
||
# ports are passed.
|
||
tcpPorts = [
|
||
80
|
||
443
|
||
];
|
||
udpPorts = [ 443 ];
|
||
|
||
# Internal parameters used by the firewall that mustn't be used by any
|
||
# other application, or in your own firewall configuration, for example if
|
||
# you are doing some kind of advanced setup on a router.
|
||
queue = 200;
|
||
desyncFwmark = "0x40000000";
|
||
};
|
||
}
|
||
```
|
||
|
||
Since Zapret2 works using nfqueue, it's important that the queue number is not
|
||
used by any other application on the system. If the default queue number of
|
||
`200` is already used by another application, you should set the option
|
||
{option}`services.zapret2.firewall.queue` to a queue number that isn't used.
|
||
|
||
Likewise, it's also important that the desync mark used to mark
|
||
already-processed packets is also not used by any other application. Otherwise,
|
||
it may cause issues such as packet loops or some packets being ignored by the
|
||
conflicting applications. If the default desync mark of `0x40000000` is used by
|
||
another application (or within your firewall configuration), you should set the
|
||
option {option}`services.zapret2.firewall.desyncFwmark` to a desync mark that
|
||
isn't used. Note that the desync mark is expected to be a bitmask, i.e. it
|
||
should be a single bit. As such, the option uses a string instead of a number,
|
||
and the module checks that it only has a single bit set in it.
|
||
|
||
The firewall configuration in the module only supports nftables, it does not
|
||
support iptables. If you wish to use iptables, or otherwise want to define your
|
||
own firewall configuration (for example, if you are running Zapret2 server-side
|
||
instead of client-side), you can disable the built-in firewall configuration by
|
||
setting {option}`services.zapret2.firewall.configureAutomatically` to `false`.
|
||
Note that, even if the option is disabled, the module still uses the queue
|
||
number and desync mark defined in the module's options, as they are passed as
|
||
command-line arguments to Zapret2. None of the other firewall options are used.
|
||
|
||
[1]: https://github.com/bol-van/zapret2/blob/master/docs/manual.en.md
|
||
[2]: https://github.com/bol-van/zapret2/blob/master/docs/manual.en.md#traffic-processing-scheme
|
||
[3]: https://github.com/bol-van/zapret2/tree/master/lua
|
||
|