-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cmd/jafar: adapt README.md and Dockerfile from original repo
This is part of #356. I have only lightly edited the README.md file to reflect the fact that this is a subdirectory rather than a standalone project.
- Loading branch information
1 parent
8e485eb
commit 4aea1ab
Showing
2 changed files
with
261 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
FROM alpine:edge | ||
RUN apk add go git musl-dev iptables tmux bind-tools curl sudo python3 | ||
ENV GOPATH=/go GO111MODULE=on |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,258 @@ | ||
# 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 | ||
any system but it really on works on Linux. | ||
|
||
## Building | ||
|
||
We use Go >= 1.14. Jafar also needs the C library headers, | ||
iptables installed, and root permissions. | ||
|
||
With Linux Alpine edge, you can compile Jafar with: | ||
|
||
``` | ||
# apk add go git musl-dev iptables | ||
# go build -v . | ||
``` | ||
|
||
Otherwise, using Docker: | ||
|
||
``` | ||
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: | ||
|
||
``` | ||
-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. | ||
|
||
``` | ||
-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: | ||
|
||
``` | ||
-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: | ||
|
||
``` | ||
-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 | ||
|
||
``` | ||
-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 | ||
|
||
``` | ||
-uncensored-resolver-url string | ||
URL of an hopefully uncensored resolver (default "dot://1.1.1.1:853") | ||
``` | ||
|
||
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 | ||
resolver for them, which by default is `dot://1.1.1.1:853`. You can change such | ||
default by using the `-uncensored-resolver-url` command line flag. The input | ||
URL is `<transport>://<domain>[:<port>][/<path>]`. Here are some examples: | ||
|
||
* `system:///` uses the system resolver (i.e. `getaddrinfo`) | ||
* `udp://8.8.8.8:53` uses DNS over UDP | ||
* `tcp://8.8.8.8:53` used DNS over TCP | ||
* `dot://8.8.8.8:853` uses DNS over TLS | ||
* `https://doh.powerdns.com/` uses DNS over HTTPS | ||
|
||
So, for example, if you are using Jafar to censor `1.1.1.1:853`, then you | ||
most likely want to use `-uncensored-resolver-url`. | ||
|
||
## 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`. | ||
|
||
``` | ||
# ./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: | ||
|
||
``` | ||
# ./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: | ||
|
||
``` | ||
# ./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`: | ||
|
||
``` | ||
# ./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`. |