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

Emit insecure proxy event when using unverified proxies #3070

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

tushar5526
Copy link
Contributor

Fixes #2791

@tushar5526 tushar5526 marked this pull request as draft June 14, 2023 19:10
@tushar5526 tushar5526 marked this pull request as ready for review June 14, 2023 19:21
@illia-v
Copy link
Member

illia-v commented Jun 14, 2023

@tushar5526 thanks for the change! Could you please add a test similar to test.with_dummyserver.test_no_ssl.TestHTTPSWithoutSSL and a changelog entry?

Copy link
Member

@sethmlarson sethmlarson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an excellent start, thank you! 🚀 Here's a few suggestions I can think of:

InsecureProxyWarning,
)

elif not conn.is_verified:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since both can be true, can we change this elif to an if and add a testcase for both warnings being emitted in one connection?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be something off when I am using a proxy. The following snippet will raise both InsecureRequestWarning and InsecureProxyWarning after I change the elif to if.

import urllib3
proxy = urrlib3.ProxyManager('https://localhost:8443', cert_reqs='NONE')
res = proxy.request('GET', 'https://www.google.com')

Outputs the following.

/home/tushar55/projects/urllib3/src/urllib3/connectionpool.py:1100: InsecureProxyWarning: Unverified HTTPS request is being made using proxy host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
  warnings.warn(
/home/tushar55/projects/urllib3/src/urllib3/connectionpool.py:1111: InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
  warnings.warn(

Both the conn.host and conn.proxy.host is set to the same value which is localhost. conn._tunnel_host is set to www.google.com

I suspect this line is resetting the https://github.com/urllib3/urllib3/blob/main/src/urllib3/connectionpool.py#L1062 updates the host and port when proxy is being used. Can you clarify, what is the intended behaviour here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more examples that I tested out locally after setting up a simple python http server on port 8000 using python -m http.server

import urllib3
proxy = urllib3.ProxyManager('https://localhost:8443', ca_certs='/home/tushar55/projects/urllib3/dummyserver/certs/cacert.pem', cert_reqs='NONE')
res = proxy.request('GET', 'http://127.0.0.1:8000')
print(res.status, res.data)

This raises the following error -

/home/tushar55/projects/urllib3/src/urllib3/connectionpool.py:1146: InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhos
t'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
  warnings.warn(

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely confusing, but in the case of a proxied HTTPS origin we use CONNECT tunneling so the actual destination host we want to connect to is HTTPConnection._tunnel_host. There might need to be some modifications to a warning message in order to pick the right host to use. Hopefully this helps?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think it's clear now. So, if certificate verification is disabled for a proxy manager then it should throw both the InsecureProxy and InsecureRequest warnings, right?

proxy = urllib3.ProxyManager('https://localhost:8443', cert_reqs='NONE')
res = proxy.request('GET', 'https://localhost:8000')

and for normal PoolManger, it should just throw InsecureRequest warning.

pool = urllib3.PoolManager(cert_reqs='NONE')
res = pool.request('GET', 'https://localhost:8000')

I will update the InsecureRequest warning's message to host or _tunnel_host depending upon whether we are using an HTTPS proxy or not.

test/with_dummyserver/test_no_ssl.py Outdated Show resolved Hide resolved
def test_simple_proxy(self) -> None:
https_proxy_url = f"https://{self.proxy_host}:{int(self.https_proxy_port)}"
with urllib3.ProxyManager(https_proxy_url, cert_reqs="NONE") as pool:
with pytest.warns(InsecureProxyWarning):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make an assertion about the message of InsecureProxyWarning (including it containing the right host) as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added checks in the recent commits.

) as pool:
with pytest.warns(InsecureRequestWarning):
try:
pool.request("GET", "/")
except urllib3.exceptions.SSLError as e:
assert "SSL module is not available" in str(e)

def test_simple_proxy(self) -> None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to test both HTTPS->HTTP and HTTPS->HTTPS proxy scenarios.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the expected HTTPS -> HTTP proxy scenario? Currently, it throws an InsecureRequestWarning as conn.is_verified is set to False while using HTTPs->HTTP connection. Should it only throw InsecureProxyWarning ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, this Seth's suggestion is related to #3070 (comment).
You change the message for InsecureRequestWarning, it would be good to test what host is included in the message when a proxy is used as well as changing test_simple to check what host is used there.

@@ -21,13 +21,22 @@ def test_simple(self) -> None:
assert r.status == 200, r.data


class TestHTTPSWithoutSSL(HTTPSDummyServerTestCase, TestWithoutSSL):
class TestHTTPSWithoutSSL(HTTPDummyProxyTestCase, TestWithoutSSL):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to add some assertions to existing HTTPS proxy tests as well, specifically it'd be good to assert that the InsecureProxyWarning isn't raised when using the proper configuration for secure proxies.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I have added them.

…TTPs proxy is being used.

fix: Raise warnings for both Unverified Proxy and Request
@tushar5526 tushar5526 marked this pull request as draft June 19, 2023 21:17
@tushar5526 tushar5526 marked this pull request as ready for review July 14, 2023 11:35
@tushar5526
Copy link
Contributor Author

Hi @sethmlarson , it seems that proxy_is_verified is never set for HTTPs to HTTP proxies. proxy_is_verified is set only at https://github.com/urllib3/urllib3/blob/main/src/urllib3/connection.py#L694 and the function _connect_tls_proxy containing this line is called only when we are creating a HTTP tunnel https://github.com/urllib3/urllib3/blob/main/src/urllib3/connection.py#L672. We would have to set the value for proxy_is_verified for scenarios when we are not using a TLS-in-TLS tunnel and instead setting up a TLS connection for HTTPs to HTTP case.

Copy link
Member

@illia-v illia-v left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tushar5526 interesting, it does look like a bug that HTTPS proxy with a verified certificate + HTTP destination sets is_verified=True and proxy_is_verified=None. I'd expect is_verified=False and proxy_is_verified=True according to https://github.com/urllib3/urllib3/blob/main/notes/connection-lifecycle.md

def test_simple_proxy(self) -> None:
https_proxy_url = f"https://{self.proxy_host}:{int(self.https_proxy_port)}"
with urllib3.ProxyManager(https_proxy_url, cert_reqs="NONE") as pool:
with pytest.warns((InsecureProxyWarning, InsecureRequestWarning)) as record:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes pytest check that at least one of the two warnings is raised, not that both of them are raised sequentially

@tushar5526
Copy link
Contributor Author

@tushar5526 interesting, it does look like a bug that HTTPS proxy with a verified certificate + HTTP destination sets is_verified=True and proxy_is_verified=None. I'd expect is_verified=False and proxy_is_verified=True according to https://github.com/urllib3/urllib3/blob/main/notes/connection-lifecycle.md

Yes, thanks for confirming. This has been bugging me for a long time. Should I open a new issue?

The tests for this PR is dependent on the correct behaviour of proxy_is_verifed and is_verified

@illia-v
Copy link
Member

illia-v commented Sep 19, 2023

@tushar5526 yes, an issue for this would be really appreciated

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

Successfully merging this pull request may close these issues.

Emit a warning when proxy_is_verified is False
3 participants