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

Hostname alias (CNAME) not handled properly #75

Closed
steelman opened this issue Dec 13, 2023 · 5 comments
Closed

Hostname alias (CNAME) not handled properly #75

steelman opened this issue Dec 13, 2023 · 5 comments

Comments

@steelman
Copy link

I am using requests-kerberos on top of pyspnego (and sspilib). The most basic example

import requests
from requests_kerberos import HTTPKerberosAuth, REQUIRED
kerberos_auth = HTTPKerberosAuth(mutual_authentication=REQUIRED, force_preemptive=True)
r = requests.get("https://service.example.org/", auth=kerberos_auth)

gives me errors (i am not entirely sure in which library the real problem is).

DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): service.example.com:443
send: b'GET / HTTP/1.1\r\nHost: service.example.com\r\nUser-Agent: python-requests/2.31.0\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\n\r\n'
reply: 'HTTP/1.1 401 Unauthorized\r\n'
header: Content-Type: text/html
header: Server: Microsoft-IIS/10.0
header: WWW-Authenticate: Negotiate
header: WWW-Authenticate: NTLM
header: X-Powered-By: ASP.NET
header: Date: Wed, 13 Dec 2023 12:24:48 GMT
header: Content-Length: 1293
DEBUG:urllib3.connectionpool:https://service.example.com:443 "GET / HTTP/1.1" 401 1293
DEBUG:requests_kerberos.kerberos_:handle_401(): Handling: 401
DEBUG:spnego._sspi:SSPI step input:
ERROR:requests_kerberos.kerberos_:generate_request_header(): ctx step failed:
Traceback (most recent call last):
  File "C:\Users\steelman\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\spnego\_context.py", line 68, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\steelman\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\spnego\_sspi.py", line 263, in step
    res = sspilib.raw.initialize_security_context(
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "src\\sspilib\\raw\\_security_context.pyx", line 438, in sspilib.raw._security_context.initialize_security_context

The problem is that service.example.com is an alias for host.example.com (a CNAME DNS record) and there is an SPN only for host.example.com. I have managed to work this problem around changing one line in the above example to

kerberos_auth = HTTPKerberosAuth(hostname_override="host.example.com", mutual_authentication=REQUIRED, sanitize_mutual_error_response=False)

However, I believe this shouldn't be required since the same example (without hostname_override) works fine on Debian with MIT Kerberos.

@jborean93
Copy link
Owner

This library just passes through the values given to it to the GSSAPI/SSPI implementation that it is wrapping. Based on the traceback here it's on Windows so it's just using what Windows SSPI does in the background. I know MIT Kerberos has some configurations (like dns_canonicalize_hostname) to be able to do things like have it resolve the target name but I'm unsure if SSPI has any equivalent.

Ultimately name resolution is not something this library handles at all, it just provides the name of the service given to it to the underlying C library. Whether that does a resolution or not is up to that library and it's not something I have any plans on supporting in this library. Ultimately you have four choices here

  • Resolve the alias in your script before setting the requests hostname,
  • Set the hostname override (no guarantee the alias will actually point to that server)
  • Figure out if SSPI/Windows has a way to resolve the alias for you
  • Setup an SPN for that alias and configure the services to run with the service account that the SPN is registered to

@steelman
Copy link
Author

steelman commented Dec 13, 2023

Thank you for suggestions. Let me add some context and continue this discussion. Even if not to implement any solution in your libraries, then just for future reference.

Resolve the alias in your script before setting the requests hostname,

The thing is the server is configured to work as service.example.net when accessed as host.example.com it shows a "factory default" page. The current setup works perfectly fine with Windows browsers.

Set the hostname override (no guarantee the alias will actually point to that server)

If I resolve the alias and use the value of the A record to set the override then it will work. But this really seems like something that should happen under the hood.

Figure out if SSPI/Windows has a way to resolve the alias for you

That would be the best option. I will investigate it and post my findings here if you don't mind.

Setup an SPN for that alias

Beyond my pay grade (-;

@jborean93
Copy link
Owner

The thing is the server is configured to work as service.example.net when accessed as host.example.com it shows a "factory default" page. The current setup works perfectly fine with Windows browsers.

It could potentially be that Windows resolves the alias before it itself called SSPI with the SPN value. Without seeing a trace of the call it would be hard to tell unfortunately. Regardless that would be a question for the library calling this one as we are just in charge of wrapping the C library.

But this really seems like something that should happen under the hood.

Unfortunately it can be dangerous for a few reasons

  • You are now reliant on DNS to verify the identity of the server
    • This could be dangerous if DNS has been poisoned, the whole purpose of Kerberos server verification is to verify the server is who you think it is
    • This is why MIT Kerberos offers a way to disable this, IIRC some distros like Fedora even disable it by default
  • The Alias that was resolved for the SPN name may end up being different to the alias resolved for the actual socket connection
    • It would require the client to resolve the hostname, use the same for the SPN and socket target but then set the TLS SNI hostname to the original alias
    • None of this is currently possible using requests-kerberos here
    • Using the wrong target could result in a server verification failure from Kerberos

Granted these are weak reasons but they are still valid in some cases.

Beyond my pay grade (-;

It's not too hard, granted it's also not a simple fix. Essentially you;

  • Create a service principal, like a gMSA or any AD account (gMSA is preferable)
  • Set the SPN for your alias (gMSA already does this for you when creating it in PowerShell)
  • Configure what is running the servers behind the aliases to run as the gMSA

@steelman
Copy link
Author

It could potentially be that Windows resolves the alias before it itself called SSPI with the SPN value.

Apparently it is applications responsibility to resolve CNAME records. Still, I think there is no point in separate copy of such code in every client. Thus, I've created patches for both requests-kerberos and requests-gssapi.

Unfortunately it can be dangerous for a few reasons

I believe that with Kerberos the worst that can happen is a denial-of-service attack if DNS is attacked.

Beyond my pay grade (-;

It's not too hard, granted it's also not a simple fix.

I may have misused the phrase, but this is literally not my job and I don't have rights to do it.

@jborean93
Copy link
Owner

Apparently it is applications responsibility to resolve CNAME records. Still, I think there is no point in separate copy of such code in every client. Thus, I've created patches for both requests/requests-kerberos#185 and pythongssapi/requests-gssapi#50.

It's a tricky question, I personally think this is somewhat irresponsible to say outright, you can't always trust that DNS is correct and you can't always trust that the resolved hostname is going to be who you talk to. It's also not really possible to use in a load balancer situation.

I believe that with Kerberos the worst that can happen is a denial-of-service attack if DNS is attacked.

The worst that can happen is a man in the middle situation where the client thinks they are talking to foo but in reality might be talking to bar. Server authentication is a cornerstone of modern authentication methods, not doing so is dangerous as now the client might be sending sensitive information to bar that should only be for foo.

I may have misused the phrase, but this is literally not my job and I don't have rights to do it.

I sympathize with you here but this is generally the solution when you want to use Kerberos with an alias, especially when that alias can resolve to different hosts over time. It works because now you have a single principal associated with the alias name that will be used to verify the server is who the client thinks it is.

As this library is passing along the values it uses to the underlying C library there's nothing else that I think pyspnego can do here so I'll be closing the issue.

@jborean93 jborean93 closed this as not planned Won't fix, can't repro, duplicate, stale Dec 22, 2023
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

No branches or pull requests

2 participants