2021-02-02 12:05:47 +01:00
|
|
|
# Jafar
|
|
|
|
|
|
|
|
> We stepped up the game of simulating censorship upgrading from the
|
|
|
|
> evil genius to the evil grand vizier.
|
|
|
|
|
|
|
|
Jafar is a censorship simulation tool used for testing OONI. It builds on
|
2022-02-10 17:38:51 +01:00
|
|
|
any system but it really only works on Linux.
|
2021-02-02 12:05:47 +01:00
|
|
|
|
|
|
|
## Building
|
|
|
|
|
2022-06-02 22:25:37 +02:00
|
|
|
We use Go >= 1.18. Jafar also needs the C library headers,
|
2021-02-02 12:05:47 +01:00
|
|
|
iptables installed, and root permissions.
|
|
|
|
|
|
|
|
With Linux Alpine edge, you can compile Jafar with:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
apk add go git musl-dev iptables
|
|
|
|
go build -v .
|
|
|
|
```
|
|
|
|
|
|
|
|
Otherwise, using Docker:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
docker build -t jafar-runner .
|
|
|
|
docker run -it --privileged -v`pwd`:/jafar -w/jafar jafar-runner
|
|
|
|
go build -v .
|
|
|
|
```
|
|
|
|
|
|
|
|
## Usage
|
|
|
|
|
|
|
|
You need to run Jafar as root. You can get a complete list
|
|
|
|
of all flags using `./jafar -help`. Jafar is composed of modules. Each
|
|
|
|
module is controllable via flags. We describe modules below.
|
|
|
|
|
|
|
|
### main
|
|
|
|
|
|
|
|
The main module starts all the other modules. If you don't provide the
|
|
|
|
`-main-command <command>` flag, the code will run until interrupted. If
|
|
|
|
instead you use the `-main-command` flag, you can specify a command to
|
|
|
|
run inside the censored environment. In such case, the main module
|
|
|
|
will exit when the specified command terminates. Note that the main
|
|
|
|
module will propagate the child exit code, if the child fails.
|
|
|
|
|
|
|
|
The command can also include arguments. Make sure you quote the arguments
|
|
|
|
such that your shell passes the whole string to the specified option, as
|
|
|
|
in `-main-command 'ls -lha'`. This will execute the `ls -lha` command line
|
|
|
|
inside the censored Jafar context. You can also combine that with quoting
|
|
|
|
and variables interpolation, e.g., `-main-command "echo '$USER is the
|
|
|
|
walrus'"`. The `$USER` variable will be expanded by your shell. Assuming
|
|
|
|
your user name is `paul`, then Jafar will lex the main command as `echo
|
|
|
|
"paul is the walrus"` and will execute it.
|
|
|
|
|
|
|
|
Use the `-main-user <username>` flag to select the user to use for
|
|
|
|
running child commands. By default, we use the `nobody` user for this
|
|
|
|
purpose. We implement this feature using `sudo`, therefore you need
|
|
|
|
to make sure that `sudo` is installed.
|
|
|
|
|
|
|
|
### iptables
|
|
|
|
|
|
|
|
The iptables module is only available on Linux. It exports these flags:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
-iptables-drop-ip value
|
|
|
|
Drop traffic to the specified IP address
|
|
|
|
-iptables-drop-keyword-hex value
|
|
|
|
Drop traffic containing the specified hex keyword
|
|
|
|
-iptables-drop-keyword value
|
|
|
|
Drop traffic containing the specified keyword
|
|
|
|
-iptables-hijack-dns-to string
|
|
|
|
Hijack all DNS UDP traffic to the specified endpoint
|
|
|
|
-iptables-hijack-https-to string
|
|
|
|
Hijack all HTTPS traffic to the specified endpoint
|
|
|
|
-iptables-hijack-http-to string
|
|
|
|
Hijack all HTTP traffic to the specified endpoint
|
|
|
|
-iptables-reset-ip value
|
|
|
|
Reset TCP/IP traffic to the specified IP address
|
|
|
|
-iptables-reset-keyword-hex value
|
|
|
|
Reset TCP/IP traffic containing the specified hex keyword
|
|
|
|
-iptables-reset-keyword value
|
|
|
|
Reset TCP/IP traffic containing the specified keyword
|
|
|
|
```
|
|
|
|
|
|
|
|
The difference between `drop` and `reset` is that in the former case
|
|
|
|
a packet is dropped, in the latter case a RST is sent.
|
|
|
|
|
|
|
|
The difference between `ip` and `keyword` flags is that the former
|
|
|
|
match an outgoing IP, the latter uses DPI.
|
|
|
|
|
|
|
|
The `drop` and `reset` rules allow you to simulate, respectively, when
|
|
|
|
operations timeout and when a connection cannot be established (with
|
|
|
|
`reset` and `ip`) or is reset after a keyword is seen (with `keyword`).
|
|
|
|
|
|
|
|
Hijacking DNS traffic is useful, for example, to redirect all DNS UDP
|
|
|
|
traffic from the box to the `dns-proxy` module.
|
|
|
|
|
|
|
|
Hijacking HTTP and HTTPS traffic actually hijacks based on ports rather
|
|
|
|
than on DPI. As a known bug, when hijacking HTTP or HTTPS traffic, we
|
|
|
|
do not hijack traffic owned by root. This is because Jafar runs as root
|
|
|
|
and therefore its traffic must not match the hijack rule.
|
|
|
|
|
|
|
|
When matching keywords, the simplest option is to use ASCII strings as
|
|
|
|
in `-iptables-drop-keyword ooni`. However, you can also specify a sequence
|
|
|
|
of hex bytes, as in `-iptables-drop-keyword-hex |6f 6f 6e 69|`.
|
|
|
|
|
|
|
|
Note that with `-iptables-drop-keyword`, DNS queries containing such
|
|
|
|
keyword will fail returning `EPERM`. For a more realistic approach to
|
|
|
|
dropping specific DNS packets, combine DNS traffic hijacking with
|
|
|
|
`-dns-proxy-ignore`, to "drop" packets at the DNS proxy.
|
|
|
|
|
|
|
|
### dns-proxy (aka resolver)
|
|
|
|
|
|
|
|
The DNS proxy or resolver allows to manipulate DNS. Unless you use DNS
|
|
|
|
hijacking, you will need to configure your application explicitly to use
|
|
|
|
the proxy with application specific command line flags.
|
|
|
|
|
|
|
|
```bash
|
|
|
|
-dns-proxy-address string
|
|
|
|
Address where the DNS proxy should listen (default "127.0.0.1:53")
|
|
|
|
-dns-proxy-block value
|
|
|
|
Register keyword triggering NXDOMAIN censorship
|
|
|
|
-dns-proxy-hijack value
|
|
|
|
Register keyword triggering redirection to 127.0.0.1
|
|
|
|
-dns-proxy-ignore value
|
|
|
|
Register keyword causing the proxy to ignore the query
|
|
|
|
```
|
|
|
|
|
|
|
|
The `-dns-proxy-address` flag controls the endpoint where the proxy is
|
|
|
|
listening.
|
|
|
|
|
|
|
|
The `-dns-proxy-block` tells the resolver that every incoming request whose
|
|
|
|
query contains the specifed string shall receive an `NXDOMAIN` reply.
|
|
|
|
|
|
|
|
The `-dns-proxy-hijack` is similar but instead lies and returns to the
|
|
|
|
client that the requested domain is at `127.0.0.1`. This is an opportunity
|
|
|
|
to redirect traffic to the HTTP and TLS proxies.
|
|
|
|
|
|
|
|
The `-dns-proxy-ignore` is similar but instead just ignores the query.
|
|
|
|
|
|
|
|
### http-proxy
|
|
|
|
|
|
|
|
The HTTP proxy is an HTTP proxy that may refuse to forward some
|
|
|
|
specific requests. It's controlled by these flags:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
-http-proxy-address string
|
|
|
|
Address where the HTTP proxy should listen (default "127.0.0.1:80")
|
|
|
|
-http-proxy-block value
|
|
|
|
Register keyword triggering HTTP 451 censorship
|
|
|
|
```
|
|
|
|
|
|
|
|
The `-http-proxy-address` flag has the same semantics it has for the DNS
|
|
|
|
proxy.
|
|
|
|
|
|
|
|
The `-http-proxy-block` flag tells the proxy that it should return a `451`
|
|
|
|
response for every request whose `Host` contains the specified string.
|
|
|
|
|
|
|
|
### tls-proxy
|
|
|
|
|
|
|
|
TLS proxy is a proxy that routes traffic to specific servers depending
|
|
|
|
on their SNI value. It is controlled by the following flags:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
-tls-proxy-address string
|
|
|
|
Address where the HTTP proxy should listen (default "127.0.0.1:443")
|
|
|
|
-tls-proxy-block value
|
|
|
|
Register keyword triggering TLS censorship
|
|
|
|
```
|
|
|
|
|
|
|
|
The `-tls-proxy-address` flags has the same semantics it has for the DNS
|
|
|
|
proxy.
|
|
|
|
|
|
|
|
The `-tls-proxy-block` specifies which string or strings should cause the
|
|
|
|
proxy to return an internal-erorr alert when the incoming ClientHello's SNI
|
|
|
|
contains one of the strings provided with this option.
|
|
|
|
|
|
|
|
### bad-proxy
|
|
|
|
|
|
|
|
```bash
|
|
|
|
-bad-proxy-address string
|
|
|
|
Address where to listen for TCP connections (default "127.0.0.1:7117")
|
|
|
|
-bad-proxy-address-tls string
|
|
|
|
Address where to listen for TLS connections (default "127.0.0.1:4114")
|
|
|
|
-bad-proxy-tls-output-ca string
|
|
|
|
File where to write the CA used by the bad proxy (default "badproxy.pem")
|
|
|
|
```
|
|
|
|
|
|
|
|
The bad proxy is a proxy that reads some bytes from any incoming connection
|
|
|
|
and then closes the connection without replying anything. This simulates a
|
|
|
|
proxy that is not working properly, hence the name of the module.
|
|
|
|
|
|
|
|
When connecting using TLS, the above behaviour happens after the handshake.
|
|
|
|
|
|
|
|
We write the CA on the file specified using `-bad-proxy-tls-output-ca` such that
|
|
|
|
tools like curl(1) can use such CA to avoid TLS handshake errors. The code will
|
|
|
|
generate on the fly a certificate for the provided SNI. Not providing any SNI in
|
|
|
|
the client Hello message will cause the TLS handshake to fail.
|
|
|
|
|
|
|
|
### uncensored
|
|
|
|
|
|
|
|
```bash
|
2022-06-02 22:25:37 +02:00
|
|
|
-uncensored-resolver-doh string
|
|
|
|
URL of an hopefully uncensored DoH resolver (default "https://1.1.1.1/dns-query")
|
2021-02-02 12:05:47 +01:00
|
|
|
```
|
|
|
|
|
|
|
|
The HTTP, DNS, and TLS proxies need to resolve domain names. If you setup DNS
|
|
|
|
censorship, they may be affected as well. To avoid this issue, we use a different
|
2022-06-02 22:25:37 +02:00
|
|
|
resolver for them, which by default is the one shown above. You can change such
|
|
|
|
default by using the `-uncensored-resolver-doh` command line flag. The input
|
|
|
|
URL is an HTTPS URL pointing to a DoH server. Here are some examples:
|
|
|
|
|
|
|
|
* `https://dns.google/dns-query`
|
|
|
|
* `https://dns.quad9.net/dns-query`
|
|
|
|
|
|
|
|
So, for example, if you are using Jafar to censor `1.1.1.1:443`, then you
|
|
|
|
most likely want to use `-uncensored-resolver-doh`.
|
2021-02-02 12:05:47 +01:00
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
Block `play.google.com` with RST injection, force DNS traffic to use the our
|
|
|
|
DNS proxy, and force it to censor `play.google.com` with `NXDOMAIN`.
|
|
|
|
|
|
|
|
```bash
|
|
|
|
# ./jafar -iptables-reset-keyword play.google.com \
|
|
|
|
-iptables-hijack-dns-to 127.0.0.1:5353 \
|
|
|
|
-dns-proxy-address 127.0.0.1:5353 \
|
|
|
|
-dns-proxy-block play.google.com
|
|
|
|
```
|
|
|
|
|
|
|
|
Force all traffic through the HTTP and TLS proxy and use them to censor
|
|
|
|
`play.google.com` using HTTP 451 and responding with TLS alerts:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
# ./jafar -iptables-hijack-dns-to 127.0.0.1:5353 \
|
|
|
|
-dns-proxy-address 127.0.0.1:5353 \
|
|
|
|
-dns-proxy-hijack play.google.com \
|
|
|
|
-http-proxy-block play.google.com \
|
|
|
|
-tls-proxy-block play.google.com
|
|
|
|
```
|
|
|
|
|
|
|
|
Run `ping` in a censored environment:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
# ./jafar -iptables-drop-ip 8.8.8.8 -main-command 'ping -c3 8.8.8.8'
|
|
|
|
```
|
|
|
|
|
|
|
|
Run `curl` in a censored environment where it cannot connect to
|
|
|
|
`play.google.com` using `https`:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
# ./jafar -iptables-hijack-https-to 127.0.0.1:443 \
|
|
|
|
-tls-proxy-block play.google.com \
|
|
|
|
-main-command 'curl -Lv http://play.google.com'
|
|
|
|
```
|
|
|
|
|
|
|
|
For more usage examples, see `../../testjafar.bash`.
|