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
base: main
Are you sure you want to change the base?
Conversation
@tushar5526 thanks for the change! Could you please add a test similar to |
There was a problem hiding this 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:
src/urllib3/connectionpool.py
Outdated
InsecureProxyWarning, | ||
) | ||
|
||
elif not conn.is_verified: |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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(
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
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): |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
test/with_dummyserver/test_no_ssl.py
Outdated
@@ -21,13 +21,22 @@ def test_simple(self) -> None: | |||
assert r.status == 200, r.data | |||
|
|||
|
|||
class TestHTTPSWithoutSSL(HTTPSDummyServerTestCase, TestWithoutSSL): | |||
class TestHTTPSWithoutSSL(HTTPDummyProxyTestCase, TestWithoutSSL): |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
Hi @sethmlarson , it seems that |
There was a problem hiding this 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: |
There was a problem hiding this comment.
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
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 |
@tushar5526 yes, an issue for this would be really appreciated |
Fixes #2791