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
Determining the IP address of server with bad TLS cert. #4939
Comments
I wrote a thing to handle this, sort of: https://github.com/lifehackjim/cert_human/ You can get a cert from a server (regardless of it's validity), then perform whatever validation, reporting, or what-have you on it. Ex:
|
Thanks for your input. That doesn't seem to respond to the question "what's the IP address of the server that just failed my TLS certificate validation": the important part here is the that just failed (and, thus, resulted in a If, facing such failure, the code issues a subsequent request -- be it with PS: I do not want to validate TLS certificates in my code. I'd rather delegate that to |
You can do that, by having cert_human always include the cert attributes in the raw object of each response, but you'd have to make two requests per connection. One with verify=False first (either by using cert_human.get_response(), or by using requests.get(verify=False), then your actual connection. Ex:
|
Thanks again Jim, for your prompt feedback. AFAICT, your code does not address the issue at all. Let me try to restate it:
Minimal code example with a "fill in the blanks" approach: import requests
try:
# TCP connection to one of multiple IPs that DNS resolves `multiple.example.net` to.
resp = requests.get('https://multiple.example.net')
except requests.exceptions.SSLError:
# TLS certificate validation failed.
ip_address = ??? # Which IP address gave us a non-valid TLS certificate? PS: Not sure if the underlying connection pooling and eventual retrying that may be taking place (?) turns this into a more complex problem that what it may appear to be at first sight. |
Ah I understand now.. I didn't catch the part that you were making a request to a DNS name with multiple A records. Apologies. I don't know that any layer exposes the actual IP address that the socket is connected to (or it's just buried too deep for my quick search). But if you can find that layer, it looks like you'd have to monkey patch and bubble it up (similar to what I do with cert_human). |
Could you patch |
I decided to play around with this, because curiosity always gets the best of me. import requests
import urllib3
import ssl
_ssl_wrap_socket = urllib3.connection.ssl_wrap_socket
def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None,
ca_certs=None, server_hostname=None,
ssl_version=None, ciphers=None, ssl_context=None,
ca_cert_dir=None):
"""Pass."""
try:
return _ssl_wrap_socket(
sock=sock,
keyfile=keyfile,
certfile=certfile,
cert_reqs=cert_reqs,
ca_certs=ca_certs,
server_hostname=server_hostname,
ssl_version=ssl_version,
ciphers=ciphers,
ssl_context=ssl_context,
ca_cert_dir=ca_cert_dir,
)
except ssl.SSLError as e:
e.laddr = sock.getsockname()
e.raddr = sock.getpeername()
raise
urllib3.connection.ssl_wrap_socket = ssl_wrap_socket
url = "https://cyborg"
try:
r = requests.get(url)
except requests.exceptions.SSLError as exc:
print("Invalid cert at {!r}".format(url))
print("Local ip {} port {}".format(*exc.args[0].reason.args[0].laddr))
print("Remote ip {} port {}".format(*exc.args[0].reason.args[0].raddr)) This outputs: python moo.py
Invalid cert at 'https://cyborg'
Local ip 192.168.1.174 port 53151
Remote ip 192.168.1.32 port 443 |
Seth, Jim, Thanks for your ideas. They both follow monkey-patching approaches which I am explicitly trying to avoid: who's to say that, in the future, a given patch will work against a future I myself had created a monkey-patch based solution, patching import socket
import requests
_socket_connect_method = socket.socket.connect
def _socket_connect_tracker(self, address):
_socket_connect_tracker.address = address
return _socket_connect_method(self, address)
socket.socket.connect = _socket_connect_tracker
try:
resp = requests.get('https://multiple.example.net')
except requests.exceptions.SSLError:
print('TLS validation failed for', _socket_connect_tracker.address) Pros:
Cons:
What I was wondering and asking about in this issue was:
|
Preliminary notes:
requests
!Scenario:
The issue:
requests.get('https://multiple.example.net/')
will sometimes succeed TLS validation and other times fail. How to determine the IP address of the server with the bad TLS certificate?Why?
The "give me an IP address" solutions I found all assume the HTTP connection has been established and are mostly based in the idea of using streaming mode to get to the underlying socket, calling
getpeername()
from there.Those do not work in this scenario given that an
requests.exceptions.SSLError
exception is properly raised and there's no response object to work with from that point on. Unless the exception holds a reference to the socket, but I couldn't find it there.Questions:
requests.exceptions.SSLError
include a reference to the socket that lead to the failure, pretty much like all other requests exceptions include a reference to the request and response objects?Thanks in advance.
The text was updated successfully, but these errors were encountered: