Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add experimental TLS mode #270

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,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.
5 changes: 4 additions & 1 deletion client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ client := toxiproxy.NewClient("localhost:8474")

You can then create a new proxy using the client:
```go
proxy := client.CreateProxy("redis", "localhost:26379", "localhost:6379")
proxy, err := client.CreateProxy("redis", "localhost:26379", "localhost:6379")
if err != nil {
panic(err)
}
```

For large amounts of proxies, they can also be created using a configuration file:
Expand Down
Loading