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

Add Kerberos loopback test #71824

Merged
merged 11 commits into from
Aug 3, 2022
Merged

Add Kerberos loopback test #71824

merged 11 commits into from
Aug 3, 2022

Conversation

filipnavara
Copy link
Member

@filipnavara filipnavara commented Jul 8, 2022

Introduces an infrastructure to run Kerberos.NET powered KDC inside a unit test and to redirect the system GSSAPI libraries to connect to it. This enables testing Kerberos scenarios without special Docker containers and also allows testing on macOS which had no coverage.

TODO:

  • Figure out how to consume Kerberos.NET (Arcade or at least publishing into the dotnet feeds)
  • Wire logging output into xUnit to make diagnostics easier
  • Add README
  • Update code style / fix up any credential scan issues
  • Remove unused bits of the test infrastructure (PKINIT, PAC)
  • Make the fake classes more robust and specify users explicitly
  • Fix the test to run on macOS again

Contributes to #71453

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Jul 8, 2022
@ghost
Copy link

ghost commented Jul 8, 2022

Tagging subscribers to this area: @dotnet/ncl, @vcsjones
See info in area-owners.md if you want to be subscribed.

Issue Details

TODO:

  • Figure out how to consume Kerberos.NET (Arcade or at least publishing into the dotnet feeds)
  • Wire logging output into xUnit to make diagnostics easier

Contributes to #71453

Author: filipnavara
Assignees: -
Labels:

area-System.Net.Security, community-contribution

Milestone: -

@wfurt
Copy link
Member

wfurt commented Jul 11, 2022

cc: @SteveSyfuhs in case we publish to some internal feds as well beyond NuGet

@wfurt
Copy link
Member

wfurt commented Jul 11, 2022

contributes to #68067. If we can run Kerberos tests during normal run, we don't need special reporting (and possibly separate pipeline)

@filipnavara
Copy link
Member Author

I've played with this a bit and some of the enterprise tests can be converted to use this approach. I'm still planning to see if I can get the same thing done for NTLM.

@SteveSyfuhs
Copy link

cc: @SteveSyfuhs in case we publish to some internal feds as well beyond NuGet

Have at it. Let me know if there's anything you need.

@filipnavara filipnavara marked this pull request as ready for review July 12, 2022 09:32
@filipnavara
Copy link
Member Author

filipnavara commented Jul 12, 2022

Not sure what are the guidelines for code style in tests. In particular, I used var and file-scoped namespaces. I'm fine with changing it to match preferred code style. Similarly, I am not sure what password usage would trip the credential scans, so I reused the passwords from the enterprise tests that prominently contain the word PLACEHOLDER.

Coding style guidelines don't seem to mention the file-scoped namespaces (cc @stephentoub).

@filipnavara filipnavara requested a review from wfurt July 12, 2022 09:34
@filipnavara
Copy link
Member Author

filipnavara commented Jul 12, 2022

I can also split off c6619e1 into separate PR to make it easier to review. Thoughts?

@wfurt
Copy link
Member

wfurt commented Jul 12, 2022

I'm not sure if we should change the production code at this point. I would be happy with getting some coverage with Kerberos.Net. e.g. splitting/postponing c6619e1. I'm on the road once again, I should be able to take closer look by and of the week.

@filipnavara
Copy link
Member Author

filipnavara commented Jul 12, 2022

I'm not sure if we should change the production code at this point. I would be happy with getting some coverage with Kerberos.Net. e.g. splitting/postponing c6619e1.

Alright, I will split it (#72024). The reasoning behind c6619e1 is that it's a behavior difference between Unix-based OSes and Windows. Fixing it allows to test incorrect credentials which is test coverage for #71484. Without the change it's only possible to specify Negotiate authentication package which will fallback to NTLM instead of reporting the error from invalid credentials.

Copy link
Member

@wfurt wfurt left a comment

Choose a reason for hiding this comment

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

generally looks good to me.
sorry for the long delay @filipnavara.

public FakeKdcServer(KdcServerOptions serverOptions)
{
_kdcServer = new KdcServer(serverOptions);
_tcpListener = new TcpListener(System.Net.IPAddress.Loopback, 0);
Copy link
Member

Choose a reason for hiding this comment

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

nit: IPAddress.Loopback? We may eventually figure out how to do dual mode but this is ok for now IMHO

{
_kdcServer = new KdcServer(serverOptions);
_tcpListener = new TcpListener(System.Net.IPAddress.Loopback, 0);
_runningLock = new object();
Copy link
Member

Choose a reason for hiding this comment

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

Do we need new object?
object _runningLock => TcpListener ?

Copy link
Member Author

Choose a reason for hiding this comment

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

I guess we don't. It's a habit not to reuse other objects for locks because it's prone to misuse but I can change it.


class FakeKerberosPrincipal : IKerberosPrincipal
{
private readonly byte[] _password;
Copy link
Member

Choose a reason for hiding this comment

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

nit: I would change the name to decrease chance we get flagged by security scans.

Choose a reason for hiding this comment

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

RFC-wise it's called "long term secret"


var krbName = KrbPrincipalName.FromString($"krbtgt/{predictedRealm}");

return new FakeKerberosPrincipal(PrincipalType.Service, krbName.FullyQualifiedName, predictedRealm, new byte[16]);
Copy link
Member

Choose a reason for hiding this comment

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

should we initialize the password to something? (may not matter but looks odd to me)

Choose a reason for hiding this comment

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

It's fine as long as it's consistently x00 everywhere and is never used for anything in production.

_principalService = new FakePrincipalService(realm);

byte[] krbtgtPassword = new byte[16];
//RandomNumberGenerator.Fill(krbtgtPassword);
Copy link
Member

Choose a reason for hiding this comment

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

remove? Or set to some string if we need decrypt for debugging.

_krb5Path = Path.GetTempFileName();
File.WriteAllText(_krb5Path,
OperatingSystem.IsLinux() ?
$"[realms]\n{_options.DefaultRealm} = {{\n master_kdc = {endpoint}\n kdc = {endpoint}\n}}\n" :
Copy link
Member

Choose a reason for hiding this comment

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

Why this depends on OS?

Copy link
Member Author

Choose a reason for hiding this comment

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

Different notation to force TCP over UDP and to specify the KDC as master.

}

// Set environment variables for GSSAPI
Environment.SetEnvironmentVariable("KRB5_CONFIG", _krb5Path);
Copy link
Member

Choose a reason for hiding this comment

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

does this actually work? Last time (long time ago) I check this did not change the actual ENV e.g. was invisible to native bits.

Copy link
Member Author

@filipnavara filipnavara Aug 2, 2022

Choose a reason for hiding this comment

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

Yes. That's why the test run inside RemoteExecutor. Environment.SetEnvironmentVariable doesn't call setenv but for newly launched processes it is used and that's what remote executor does.

@wfurt wfurt merged commit 84eb453 into dotnet:main Aug 3, 2022
@karelz karelz added this to the 7.0.0 milestone Aug 7, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Sep 6, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Net.Security community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants