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

AuTest suite defaults to TLSv1.0? #288

Closed
Rwardc opened this issue Oct 12, 2023 · 11 comments
Closed

AuTest suite defaults to TLSv1.0? #288

Rwardc opened this issue Oct 12, 2023 · 11 comments
Labels

Comments

@Rwardc
Copy link

Rwardc commented Oct 12, 2023

I'm trying to leverage the test suite from this project to test a local http proxy project, but I have some questions:

  • When running the tests, the verifier client defaults to TLSv1.0 in its handshake. Is there a way to specify a minimum TLS version? It looks as though its ignoring the system default for TLS.
  • Currently the tests spin up their own python based proxy. Is there a simple way to point it to a different process instead?
@bneradt
Copy link
Collaborator

bneradt commented Oct 12, 2023

Thanks for your interest in the project. Just to make sure we're in sync, are you interacting with Proxy Verifier on current master at the moment?

  • The client uses TLS_client_method per https.cc:796. As such it should negotiate the highest level of TLS supported by your proxy. There is not currently a way to specify a minimum TLS version, but we can investigate adding that feature if it will be helpful.
  • There is no way to point to a different test proxy. The tests rely upon pretty ad-hoc, custom behavior from the test proxy (such as dynamic header manipulation in some circumstances, specific logging of the proxy traffic in expected order, etc.) which would be difficult for another proxy to support.

Oh, about point 1: perhaps I misunderstood. You're not discussing a production proxy negotiating TLS 1.0, you're saying that the test proxy is negotiating TLS 1.0. Is that it? If that is the case, we can investigate what can be done to increase that version negotiated by the test proxy.

@Rwardc
Copy link
Author

Rwardc commented Oct 12, 2023

Yeah, its possible I misinterpreted what this tool is supposed to do. I was hoping to use the test suite, since it already contains a bunch of the tests that we'd need to implement to validate our production proxy anyways.

As far as the TLS handshake negotiation, I was running a verifier client using the replay files from one of the simple HTTP tests while directing it to connect to our production proxy. I was surprised to see that that the client attempts to connect first with TLSv1.0, which we don't support (which generates a 400 back from our proxy). I would have assumed the verifier client would have picked the system minimum version, which is 1.2 on mine.

So does this mean that the TLS version is somehow controlled by the test replay file (the yaml), or is this negotiation integral to the client?

@bneradt
Copy link
Collaborator

bneradt commented Oct 12, 2023

Right. There is some potential confusion in terminology and concepts, so let me try to be helpful and make things explicit:

  1. Proxy Verifier is the end product of this git repo and is itself a test tool. It is made to verify the behavior of a proxy by replaying traffic and raising errors and non-zero return codes if the Proxy doesn't behave as the verification directives in the replay file dictates.
  2. Proxy Verifier itself has a test suite written in autest. We need to be able to verify the test tool's (Proxy Verifier's) functionality, so Proxy Verifier has a suite of autests written in Python that has as a dummy Python proxy and that prints information about what Proxy Verifier client and server are sending as traffic so we can verify correct replay and verification behavior. This test suite is helpful for development so we don't break functionality but likely not very helpful to others outside of Proxy Verifier because it is special-built to verify Proxy Verifier's behavior.

Having said that:

As far as the TLS handshake negotiation, I was running a verifier client using the replay files from one of the simple HTTP tests while directing it to connect to our production proxy. I was surprised to see that that the client attempts to connect first with TLSv1.0, which we don't support (which generates a 400 back from our proxy). I would have assumed the verifier client would have picked the system minimum version, which is 1.2 on mine.

Concerning the above, this concerns Proxy Verifier not its autest. In theory this is all handled by OpenSSL via TLS_client_method. Proxy Verifier doesn't implement its own TLS negotiation. I can look into this further.

@Rwardc
Copy link
Author

Rwardc commented Oct 12, 2023

Yes, I had noticed the use of the TLS_client_method, which is why this behavior surprised me. Below is a screenshot from wireshark, showing that the verifier client attempted to connect to port 31020 (our proxy) starting with TLSv1.0. The bad request response comes from our HTTP library (Rustls), which has disabled support for 1.0.

image

@bneradt
Copy link
Collaborator

bneradt commented Oct 12, 2023

I believe that { 3, 1 } version is for backwards compatibility with older servers. The client advertises the version it supports in the client hello via the version in the handshake, which your screenshot just misses, but the bytes are visible 6 later after the highlighted 03 01: 03 03. Thus it looks like your client is advertising TLS 1.2 support to the server, as you'd expect. That's what my client advertises too:

Screenshot 2023-10-12 at 17 31 19

As an aside, are you using the prebuilt binaries? If you are building proxy verifier from source, are you building with the libraries installed via tools/build_library_dependencies.sh?

@bneradt
Copy link
Collaborator

bneradt commented Oct 12, 2023

which we don't support (which generates a 400 back from our proxy).

It replies with a 400 HTTP response? That would imply that it completed the handshake. So the server negotiated some TLS version. What version do you see in the SERVER_HELLO? That will indicate the version that the client and server agreed upon.

Here's an example from my packet capture showing TLS 1.2 negotiation:

Screenshot 2023-10-12 at 17 55 31

@bneradt
Copy link
Collaborator

bneradt commented Oct 16, 2023

@Rwardc : any update on this? Have you been able to see what TLS version the client and server are negotiating in the handshake?

@Rwardc
Copy link
Author

Rwardc commented Oct 16, 2023

So in the example I posted, there's no server hello coming back, just the 400 response. I'm not sure why that's happening, but I assume its something to do with our proxy server.

On a somewhat related note, I'm trying my hand at writing some yaml for our tests, but the errors returned for bad nodes are cryptic at best:

terminate called after throwing an instance of 'YAML::InvalidNode'
  what():  invalid node; this may result from using a map iterator as a sequence iterator, or vice-versa
Aborted (core dumped)

Do you have a validation tool you use, or is there a way to increase the verbosity of the error output?

I'm trying to create a "minimally viable" test file to get my feet wet, and I'm probably making a simple mistake somewhere. Here are the file contents:

meta:
  version: '1.0'


sessions:

- protocol: [ { name: tcp }, {name: ip} ]

  transactions:
    client-request:
      method: GET
      url: http://127.0.0.1
      version: '1.1'
      headers:
        fields:
        - [ Host, 127.0.0.1:8080 ]
        - [ Connection, Keep-Alive ]
      content:
        size: 0

    proxy-request:
      headers:
        fields:
        - [ Host, 127.0.0.1:8080 ]
        - [ Connection, Keep-Alive ]
      method: GET
      url: http://127.0.0.1
      version: '1.1'
      content:
        size: 0

    proxy-response:
      content:
        encoding: plain
        size: 0
      headers:
        fields:
        - [ Connection, keep-alive ]
      reason: OK
      status: 200

    server-response:
      content:
        encoding: plain
        size: 0
      headers:
        fields:
        - [ Connection, keep-alive ]
      reason: OK
      status: 200

@bneradt
Copy link
Collaborator

bneradt commented Oct 17, 2023

So in the example I posted, there's no server hello coming back, just the 400 response. I'm not sure why that's happening, but I assume its something to do with our proxy server.

By 400 response, I assume you mean an HTTP 400 response. If that is the case, then that can only happen after TLS is negotiated. You demonstrated that you have a tcpdump for the handshake. Please look at the SERVER_HELLO per my example above (see my screenshot) and you should be able to see what TLS version it negotiated.

On a somewhat related note, I'm trying my hand at writing some yaml for our tests, but the errors returned for bad nodes are cryptic at best:

terminate called after throwing an instance of 'YAML::InvalidNode'
what(): invalid node; this may result from using a map iterator as a sequence iterator, or vice-versa
Aborted (core dumped)

This is a Proxy Verifier bug. It's not supposed to be this cryptic. You are exposing a crash while parsing the file.

In any case, looking at your file, there are two issues:

  • transactions take a sequence. Because you don't start client-request with a -, yaml thinks you are passing it a map. This causes the abort.
  • Proxy Verifier needs each transaction to have a key. Add a uuid header field.

This diff to your file should get your replay file parsing and working:

$  diff /tmp/old /tmp/new                                             
10c10
<     client-request:
---
>   - client-request:
17a18
>         - [ uuid, first-transaction ]

Do you have a validation tool you use, or is there a way to increase the verbosity of the error output?

In general I just massage the files until Proxy Verifier is happy parsing it.

Thank you again for your interest in this project.

@bneradt
Copy link
Collaborator

bneradt commented Oct 17, 2023

On a somewhat related note, I'm trying my hand at writing some yaml for our tests, but the errors returned for bad nodes are cryptic at best:

terminate called after throwing an instance of 'YAML::InvalidNode'
what(): invalid node; this may result from using a map iterator as a sequence iterator, or vice-versa
Aborted (core dumped)

Thanks for pointing this out. I filed #289 for this and fixed with #290 . This is now landed in the master branch and will be in the next release.

In the meantime, the abort can be avoided by making the transaction elements in transactions sequences by starting them with -.

Thank you again for the feedback.

@Rwardc Rwardc closed this as completed Oct 25, 2023
@bneradt
Copy link
Collaborator

bneradt commented Oct 25, 2023

Yes, I had noticed the use of the TLS_client_method, which is why this behavior surprised me. Below is a screenshot from wireshark, showing that the verifier client attempted to connect to port 31020 (our proxy) starting with TLSv1.0. The bad request response comes from our HTTP library (Rustls), which has disabled support for 1.0.

image

You know what...thinking about this further, it actually seems like your proxy has disabled TLS support in general, at least for that port. Not just for TLS 1.0. It seems to expect a plaintext HTTP request, gets a TLS CLIENT_HELLO, considers it garbled HTTP bytes, and replies with an HTTP 400 response because it considers the request malformed.

That being the case, you can either configure Proxy Verifier to talk plain HTTP (no TLS) to the proxy, or configure the proxy to expect TLS on the listening port.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants