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

Use CNAME for SPN #270

Closed
jboelter opened this issue Feb 17, 2019 · 5 comments
Closed

Use CNAME for SPN #270

jboelter opened this issue Feb 17, 2019 · 5 comments

Comments

@jboelter
Copy link

Thank you for the excellent library. I encountered an issue where the hostname of the http endpoint I'm interacting with is not the same as the SPN. The SPN in this case is something like server.site.example.com that can be discovered through a LookupCNAME of foo.example.com.

It's also not clear that the current implementation will work with an empty spn and non-relative redirects. The Host header is cleared on redirects by the go http client unless the redirect is relative. client.go#L569 and Go bug 22233

I think it would useful to have a few options on the SPNEGO spnego.NewClient similiar to the client settings approach:

  • UseCNAME()
  • UseHost()
  • UseSPN(string spn)

This is on v7.2.0

	cfg, _ := config.NewConfigFromString(krb5conf)
	cl := client.NewClientWithPassword("username", "REALM.COM", "password", cfg, client.AssumePreAuthentication(true))
	r, _ := http.NewRequest("GET", "https://foo.example.com", nil)
	spnegoCl := spnego.NewClient(cl, nil, "")
	resp, err := spnegoCl.Do(r)

I quick hack got this working -- in this case, the negotiation is occurring after a redirect, so I'm pulling the name out of r.URL.Host.

func SetSPNEGOHeader(cl *client.Client, r *http.Request, spn string) error {
	if spn == "" {
		name, err := net.LookupCNAME(r.URL.Host)
		if err != nil {
			return fmt.Errorf("could not lookup cname for url: %v", err)
		}
		name = strings.TrimSuffix(name, ".")
		spn = "HTTP/" + name
	}
	// ...
}
@jcmturner
Copy link
Owner

When creating an SPNEGO client you can override the SPN as below with the 3rd argument value:

spnegoCl := spnego.NewClient(cl, nil, "HTTP/host.test.gokrb5")

Here the SPN is set to HTTP/host.test.gokrb5 so if the service has a different SPN registered than what would be automatically derived from the URL this can be used. For example...

var spn string
name, err := net.LookupCNAME(r.URL.Host)
if err == nil {
	spn = "HTTP/" + strings.TrimSuffix(name, ".")
}
spnegoCl := spnego.NewClient(cl, nil, spn)

The spnego.Client was introduced in v7 but the spn argument to the SetSPNEGOHeader in v6 would work the same way.

The spnego.Client in v7 was introduced to handle behaviour of HTTP redirects.

@jboelter
Copy link
Author

In this situation the negotiation is happening on a redirect where I don't know the SPN ahead of time depending on the auth provider that the site redirects to (i.e. auth.example.com could also be auth-dev.example.com).

GET foo.example.com -> 302 auth.example.com
GET auth.example.com [Negotiate happens here] -> 302 foo.example.com?token=xyz
GET foo.example.com?token=xyz -> 200 OK

Is there way to set the correct SPN dynamically during this redirect sequence w/ the current library?

@jcmturner
Copy link
Owner

So if I have understood this correctly there are two things going on:

  1. HTTP redirecting
  2. CNAME resolution where the SPN is the A-record not the CNAME value.

Is this the case?
If so I think you are right that the net.LookupCNAME logic is needed in the SetSPNEGOHeader.

@jcmturner
Copy link
Owner

I have updated and released v7.2.1 which should address this. The solution is very similar to the code you first posted. The correct behaviour is to use the underlying canonical name. The SPN value still can be overridden if needs be when creating the spnego client.

Let me know if this works for you so this issue can be closed.

@jboelter
Copy link
Author

Finally had a chance to come back to this -- works perfectly. Thank you for the fix. The fallback to hostname if the CNAME lookup fails looks good.

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