Skip to content

Commit

Permalink
Rebased pull request
Browse files Browse the repository at this point in the history
Shopify#270

With small modifications for new APIs and new toxic that drops connection after a timeout
  • Loading branch information
nmorenor committed Feb 13, 2024
1 parent 7abcb6f commit 1f4f990
Show file tree
Hide file tree
Showing 6 changed files with 412 additions and 12 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ dist/
vendor/

tmp/

config/

.vscode/
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,31 @@ An example `config/toxiproxy.json`:
]
```

An example `config/toxiproxy.json` with the experimental TLS feature:

```json
[
{
"name": "plain",
"listen": "[::]:1080",
"upstream": "www.arnes.si:80",
"enabled": true
},
{
"name": "ssl",
"listen": "[::]:1443",
"upstream": "www.arnes.si:443",
"enabled": true,
"tls": {
"cert": "./cert.crt",
"key": "./cert.key"
}
}
]
```

For more details about TLS please check [TLS.md](./TLS.md).

Use ports outside the ephemeral port range to avoid random port conflicts.
It's `32,768` to `61,000` on Linux by default, see
`/proc/sys/net/ipv4/ip_local_port_range`.
Expand Down
117 changes: 117 additions & 0 deletions TLS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# TLS

Using Toxiproxy with TLS presents its own challenges.
There are multiple ways how to use Toxiproxy in such a set-up.

## Plain-connection

That means Toxiproxy will just act as a TCP proxy. No patches are necessary.
TLS handshake will still be performed with actual endpoint. Thus Toxiproxy will
not be able to see (plain-text) traffic but may still apply toxics (like delays) to the flow.

Example `config/toxiproxy.json`
```json
[
{
"name": "quasissl",
"listen": "[::]:443",
"upstream": "www.arnes.si:443",
"enabled": true
}
]
```

In this case you need to make sure the hostname (www.arnes.si in the example)
points to Toxiproxy IP. You could use hosts file for that with an entry like

```
127.0.0.1 www.arnes.si
```

but that isn't really the best option. A more scalable solution would be to change your DNS server to return fake responses.
Easiest is probably [Coredns](https://coredns.io) with rewrite plugin.

Other option is a transparent proxy to forward specific traffic via iptables/netfilter rules.

## TLS connection with static certificate

In this mode patched Toxiproxy will terminate the TLS connection and always return the configured certificate.

Example `config/toxiproxy.json`
```json
[
{
"name": "ssl",
"listen": "[::]:443",
"upstream": "www.arnes.si:443",
"enabled": true,
"tls": {
"cert": "./cert.crt",
"key": "./cert.key"
}
}
]
```

In this case users will configure different hostname - say toxiproxy.mydomain.org instead of www.arnes.si. If you have
proper X.509 certificate for toxiproxy.mydomain.org (for instance through [Let's Encrypt](https://letsencrypt.org)) everything
will behave fine.

TLS section has an additional option:
"verifyUpstream" that is by default set to false. That is if we are already performing a Man-In-The-Middle attack it doesn't really make much
sense to be cautious about the upstream doing something similar. But you can always do something like:

```json
[
{
"name": "ssl",
"listen": "[::]:443",
"upstream": "www.arnes.si:443",
"enabled": true,
"tls": {
"cert": "./cert.crt",
"key": "./cert.key",
"verifyUpstream": true
}
}
]
```

## Dynamic certificates based on SNI

In this mode patched Toxiproxy will observe what the hostname was in the request and use the given certificate as a CA to sign the new (dummy) certificate
that matches this hostname. Currently it will generate 2048 bit RSA keypair for that purpose.

This mode is very similar to the first one (except that Toxiproxy is doing the TLS termination and can see plain-text traffic).

An example `config/toxiproxy.json`:

```json
[
{
"name": "ssl",
"listen": "[::]:443",
"upstream": "www.arnes.si:443",
"enabled": true,
"tls": {
"cert": "./cert.crt",
"key": "./cert.key",
"isCA": true
}
}
]

Here you need to do something similar to option number 1 and additionally also configure the given CA cert (still passed in the configuration as cert/key) as trusted on all machines
that will be connecting to Toxiproxy. Benefit is that you can centrally configure the interception rules (no need to change endpoints).

You could use something like [SNIproxy](https://github.com/dlundquist/sniproxy) in front which makes it easier to just forward everything to the proxy and then route just specific
stuff through Toxiproxy.

When isCA is true Toxiproxy will verify that cert.crt is actually a CA certificate (but you can always create a self-signed one of course). For now encrypted private key is not supported
(so be careful).

It is also possible to use "verifyUpstream" setting in this mode.

## Notes

Note that currently there is no option that Toxiproxy would terminate TLS connection and make a plain-text connection to the upstream as (for now) there is no use-case for it.
Loading

0 comments on commit 1f4f990

Please sign in to comment.