options: add request_client_cert to enable mutual TLS (#7175)
* options: add request_client_cert to enable mutual TLS This capability was already built-in but hard-coded to be disabled. Making it configurable as option (defaulting to off) enables mTLS connections between clients and mitmproxy. If true, mitmproxy will send a TLS `CertificateRequest` message to the client during the TLS handshake, upon which a client needs to present a client certificate to mitmproxy to successfully establish an mTLS connection. This option can be used together with the `client_certs` option to also establish an mTLS connection between mitmproxy and the upstream server. In this case, mitmproxy needs to have a full client cert, including matching private key, that is trusted and accepted by the upstream server. This is a common scenario with MQTT or IoT connections. Example usage: $ mitmproxy --set request_client_cert=True --set client_certs=some_directory/ With `some_directory/` containing a `mqtt.example.com.pem` x509 certificate file (including private key). This allows a client connecting using mTLS, to be intercepted by mitmproxy, which is itself establishing an mTLS connection to the `mqtt.example.com` upstream server. Restricting the client_certs using a directory and PEM files named after the upstream domain, narrows down the mTLS requirement to this single domain, while leaving all other traffic through mitmproxy untouched (normal TLS without client certs). * add CHANGELOG entry * docs++ * swap section order, re-add example --------- Co-authored-by: Maximilian Hils <git@maximilianhils.com> Co-authored-by: Maximilian Hils <github@maximilianhils.com>
This commit is contained in:
parent
b53d2bd19a
commit
2c1802692d
|
@ -35,6 +35,8 @@
|
|||
([#7130](https://github.com/mitmproxy/mitmproxy/pull/7130), @catap)
|
||||
- Fix of measurement unit in HAR import, duration is in milliseconds
|
||||
([#7179](https://github.com/mitmproxy/mitmproxy/pull/7179), @dstd)
|
||||
- Add support for full mTLS with client certs between client and mitmproxy.
|
||||
([#7175](https://github.com/mitmproxy/mitmproxy/pull/7175), @Kriechi)
|
||||
|
||||
## 02 August 2024: mitmproxy 10.4.2
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
title: "Certificates"
|
||||
menu:
|
||||
concepts:
|
||||
weight: 3
|
||||
concepts:
|
||||
weight: 3
|
||||
---
|
||||
|
||||
# About Certificates
|
||||
|
@ -45,11 +45,9 @@ For security reasons, the mitmproxy CA is generated uniquely on the first start
|
|||
is not shared between mitmproxy installations on different devices. This makes sure
|
||||
that other mitmproxy users cannot intercept your traffic.
|
||||
|
||||
|
||||
|
||||
### Installing the mitmproxy CA certificate manually
|
||||
|
||||
Sometimes using the [quick install app](#quick-setup) is not an option and you need to install the CA manually.
|
||||
Sometimes using the [quick install app](#quick-setup) is not an option and you need to install the CA manually.
|
||||
Below is a list of pointers to manual certificate installation
|
||||
documentation for some common platforms. The mitmproxy CA cert is located in
|
||||
`~/.mitmproxy` after it has been generated at the first start of mitmproxy.
|
||||
|
@ -83,18 +81,17 @@ documentation for some common platforms. The mitmproxy CA cert is located in
|
|||
|
||||
When mitmproxy receives a request to establish TLS (in the form of a ClientHello message), it puts the client on hold
|
||||
and first makes a connection to the upstream server to "sniff" the contents of its TLS certificate.
|
||||
The information gained -- Common Name, Organization, Subject Alternative Names -- is then used to generate a new
|
||||
The information gained -- Common Name, Organization, Subject Alternative Names -- is then used to generate a new
|
||||
interception certificate on-the-fly, signed by the mitmproxy CA. Mitmproxy then returns to the client and continues
|
||||
the handshake with the newly-forged certificate.
|
||||
|
||||
Upstream cert sniffing is on by default, and can optionally be disabled by turning the `upstream_cert` option off.
|
||||
|
||||
|
||||
### Certificate Pinning
|
||||
|
||||
Some applications employ [Certificate
|
||||
Pinning](https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning) to prevent
|
||||
man-in-the-middle attacks. This means that **mitmproxy's**
|
||||
man-in-the-middle attacks. This means that **mitmproxy's**
|
||||
certificates will not be accepted by these applications without modifying them.
|
||||
If the contents of these connections are not important, it is recommended to use
|
||||
the [ignore_hosts]({{< relref "howto-ignoredomains">}}) feature to prevent
|
||||
|
@ -180,9 +177,9 @@ The `mitmproxy-ca.pem` certificate file has to look roughly like this:
|
|||
<cert>
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
When looking at the certificate with
|
||||
When looking at the certificate with
|
||||
`openssl x509 -noout -text -in ~/.mitmproxy/mitmproxy-ca.pem`
|
||||
it should have at least the following X509v3 extensions so mitmproxy can
|
||||
it should have at least the following X509v3 extensions so mitmproxy can
|
||||
use it to generate certificates:
|
||||
|
||||
X509v3 extensions:
|
||||
|
@ -198,7 +195,26 @@ openssl req -x509 -new -nodes -key ca.key -sha256 -out ca.crt -addext keyUsage=c
|
|||
cat ca.key ca.crt > mitmproxy-ca.pem
|
||||
```
|
||||
|
||||
## Using a client side certificate
|
||||
## Mutual TLS (mTLS) and client certificates
|
||||
|
||||
TLS is typically used in a way where the client verifies the server's identity
|
||||
using the server's certificate during the handshake, but the server does not
|
||||
verify the client's identity using the TLS protocol. Instead, the client
|
||||
transmits cookies or other access tokens over the established secure channel to
|
||||
authenticate itself.
|
||||
|
||||
Mutual TLS (mTLS) is a mode where the server verifies the client's identity
|
||||
not using cookies or access tokens, but using a certificate presented by the
|
||||
client during the TLS handshake. With mTLS, both client and server use a
|
||||
certificate to authenticate each other.
|
||||
|
||||
If a server wants to verify the clients identity using mTLS, it sends an
|
||||
additional `CertificateRequest` message to the client during the handshake. The
|
||||
client then provides its certificate and proves ownership of the private key
|
||||
with a matching signature. This part works just like server authentication, only
|
||||
the other way around.
|
||||
|
||||
### mTLS between mitmproxy and upstream server
|
||||
|
||||
You can use a client certificate by passing the `--set client_certs=DIRECTORY|FILE`
|
||||
option to mitmproxy. Using a directory allows certs to be selected based on
|
||||
|
@ -206,9 +222,30 @@ hostname, while using a filename allows a single specific certificate to be used
|
|||
for all TLS connections. Certificate files must be in the PEM format and should
|
||||
contain both the unencrypted private key and the certificate.
|
||||
|
||||
### Multiple client certificates
|
||||
|
||||
You can specify a directory to `--set client_certs=DIRECTORY`, in which case the matching
|
||||
certificate is looked up by filename. So, if you visit example.org, mitmproxy
|
||||
looks for a file named `example.org.pem` in the specified directory and uses
|
||||
this as the client cert.
|
||||
|
||||
### mTLS between client and mitmproxy
|
||||
|
||||
By default, mitmproxy does not send the `CertificateRequest` TLS handshake
|
||||
message to connecting clients. This is because it trips up some clients that do
|
||||
not expect a certificate request (most famously old Android versions). However,
|
||||
there are other clients -- in particular in the MQTT / IoT environment -- that
|
||||
do expect a certificate request and will otherwise fail the TLS handshake.
|
||||
|
||||
To instruct mitmproxy to request a client certificate from the connecting
|
||||
client, you can pass the `--set request_client_cert=True` option. This will
|
||||
generate a `CertificateRequest` TLS handshake message and (if successful)
|
||||
establish an mTLS connection. This option only requests a certificate from the
|
||||
client, it does not validate the presented identity in any way. For the purposes
|
||||
of testing and developing client and server software, this is typically not an
|
||||
issue. If you operate mitmproxy in an environment where untrusted clients might
|
||||
connect, you need to safeguard against them.
|
||||
|
||||
The `request_client_cert` option is typically paired with `client_certs` like so:
|
||||
|
||||
```bash
|
||||
mitmproxy --set request_client_cert=True --set client_certs=client-cert.pem
|
||||
```
|
||||
|
|
|
@ -160,6 +160,12 @@ class TlsConfig:
|
|||
help="Use a specific elliptic curve for ECDHE key exchange on server connections. "
|
||||
'OpenSSL syntax, for example "prime256v1" (see `openssl ecparam -list_curves`).',
|
||||
)
|
||||
loader.add_option(
|
||||
name="request_client_cert",
|
||||
typespec=bool,
|
||||
default=False,
|
||||
help=f"Requests a client certificate (TLS message 'CertificateRequest') to establish a mutual TLS connection between client and mitmproxy (combined with 'client_certs' option for mitmproxy and upstream).",
|
||||
)
|
||||
|
||||
def tls_clienthello(self, tls_clienthello: tls.ClientHelloData):
|
||||
conn_context = tls_clienthello.context
|
||||
|
@ -199,7 +205,7 @@ class TlsConfig:
|
|||
cipher_list=tuple(cipher_list),
|
||||
ecdh_curve=ctx.options.tls_ecdh_curve_client,
|
||||
chain_file=entry.chain_file,
|
||||
request_client_cert=False,
|
||||
request_client_cert=ctx.options.request_client_cert,
|
||||
alpn_select_callback=alpn_select_callback,
|
||||
extra_chain_certs=tuple(extra_chain_certs),
|
||||
dhparams=self.certstore.dhparams,
|
||||
|
|
|
@ -50,6 +50,7 @@ export interface OptionsState {
|
|||
proxyauth: string | undefined;
|
||||
rawtcp: boolean;
|
||||
readfile_filter: string | undefined;
|
||||
request_client_cert: boolean;
|
||||
rfile: string | undefined;
|
||||
save_stream_file: string | undefined;
|
||||
save_stream_filter: string | undefined;
|
||||
|
@ -152,6 +153,7 @@ export const defaultState: OptionsState = {
|
|||
proxyauth: undefined,
|
||||
rawtcp: true,
|
||||
readfile_filter: undefined,
|
||||
request_client_cert: false,
|
||||
rfile: undefined,
|
||||
save_stream_file: undefined,
|
||||
save_stream_filter: undefined,
|
||||
|
|
Loading…
Reference in New Issue