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

Example Authentication providers #3410

Merged
merged 65 commits into from
Mar 2, 2023
Merged

Conversation

niloc132
Copy link
Member

@niloc132 niloc132 commented Feb 6, 2023

Remaining to mark it as ready:

  • better artifact IDs for the example providers
  • documentation for the oidc provider
  • docker-compose samples that include DH itself (sub point, confirm that shaded jars work as expected)
  • psk html sample

Discussion points for reviews:

  • Do we publish these at all? that is, JAVA_PUBLIC vs JAVA_BASIC?
  • Logging standards for each provider, how noisy is too noisy? do we always emit a clickable link? do we always log what auth providers are on?
  • authType strings - okay to default to the classname for dh-provided ones, easier to user from a client (rather than guessing from the AuthHandlers value
  • futher parameterize the oidc provider to actually be generic for any oidc provider (this implies testing with other providers), or just be explicit that this is just a 'keycloak' provider?

Fixes #2564
Fixes #2934

Copy link
Member

@devinrsmith devinrsmith left a comment

Choose a reason for hiding this comment

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

I think these are mostly great. I know we've talked a little bit about the packaging changes you expect to make.

I glossed over oidc and postrges impls and JS.

I have successfully run the JS examples, as well as extended the java client to more thoroughly test psk, mtls, and anonymous.

Comment on lines +39 to +44
return Optional.of(new AuthContext() {
@Override
public LogOutput append(LogOutput logOutput) {
return logOutput.append(x509Certificates.get(0).getSubjectDN().getName());
}
});
Copy link
Member

Choose a reason for hiding this comment

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

I think we want to move towards more structured AuthContexts that capture the full state:

class MtlsAuthContext implements AuthContext {
  private final List<X509Certificate> ...
  ...
}

I know we don't have great Authorization tie-ins at the moment, but mTLS in particular is a cool case b/c multiple privileges can be attached to a single certificate.

Copy link
Member Author

Choose a reason for hiding this comment

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

Definitely agreed, but I'd like to tackle that later, and offer some base implementations along the way, with things like roles optionally populated. As it stands today, every implementation of AuthContext is functionally the same - there is no difference between Anonymous and SuperUser except what they log.

Copy link
Member

Choose a reason for hiding this comment

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

Users have to implement their own versions of AuthWiring classes. I think Devin is asking for the ability to class-cast and invoke an accessor method. I can't see a good reason to not include that at this time. If a user does indeed subclass, this is a simple java-ism that they will want to be able to do.

Copy link
Member Author

Choose a reason for hiding this comment

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

Only that I want to separate the goals here, focusing on the authentication side, making sure it works with several use cases, and follow up with authorization, as Auth(oriziation)Wiring is explicitly the second. I think we need more than "provide some subclasses and cast" to get that to make sense.


@Override
public void initialize(String targetUrl) {

Copy link
Member

Choose a reason for hiding this comment

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

Probably worth a log.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep see description, I'm hoping for some feedback on expected conventions - how noisy, do we log at all, just the URL, do we really need every handler to log roughly the same URL, etc

Copy link
Member

Choose a reason for hiding this comment

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

targetUrl is a bit weird to pass into this function. I understand we're giving psk a chance to log out a "pretty" URL, but maybe there is a better way to structure this.

My vote might be to rip out the init logging (except for Anonymous), and then have the server print targetUrl once.

);

INSERT INTO deephaven_username_password_auth.users (username, password_hash, rounds)
VALUES ('admin', '$2a$10$kjbt1Fq4k4W6EB67GDhAauuIWeI8ppx2gsi6.zLL2R5UYokek8nqO', 10);
Copy link
Member

Choose a reason for hiding this comment

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

It looks like rounds is encoded in the password_hash; rounds may be extraneous?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, unfortunately I would have to implement my own parsing, the existing bcrypt java library doesn't offer a way to read that value, so I just stored it as a separate column.

Copy link
Member

Choose a reason for hiding this comment

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

Looks like https://github.com/patrickfav/bcrypt might be a more recently maintained alternative? Quick look at the API shows that it supports parsing w/o explicitly passing in rounds and improvements https://github.com/patrickfav/bcrypt#enhancements-over-jbcrypt

Copy link
Member Author

Choose a reason for hiding this comment

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

Okay to have as an optional, very-backlogged followup?

If we're doing this "actually 100% right" there is a lot more that needs to be done too, like pooling jdbc connections, explicitly supporting other drivers, etc.

Comment on lines +39 to +44
return Optional.of(new AuthContext() {
@Override
public LogOutput append(LogOutput logOutput) {
return logOutput.append(x509Certificates.get(0).getSubjectDN().getName());
}
});
Copy link
Member

Choose a reason for hiding this comment

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

Users have to implement their own versions of AuthWiring classes. I think Devin is asking for the ability to class-cast and invoke an accessor method. I can't see a good reason to not include that at this time. If a user does indeed subclass, this is a simple java-ism that they will want to be able to do.

authentication/example-providers/oidc/README.md Outdated Show resolved Hide resolved
}

@Override
public void initialize(String targetUrl) {
Copy link
Member

Choose a reason for hiding this comment

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

I suspect we may want to force this call to be as close to the gRPC server start() call. I'd noticed back when we were originally logging the "Anonymous access is enabled" warning that it was rather early in the start up process. Other OSS products that I've seen start web servers provide such a log message near the tail of their start-up logging. We should do the same if you haven't already addressed that.

Copy link
Member Author

Choose a reason for hiding this comment

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

can you and @devinrsmith discuss a bit directly here? I definitely think these two(ish) things are true:

  • Some authentication handlers must be logged - anon must get called out noisily, and PSK stops looking like an acceptable replacement for anon if the user has to find the url, click it, go back to the log, find the PSK, copy it, paste it into the box in the browser... Both of these are best if this is the ~last thing that is logged before we hand off control to the user.
  • Most other auth handlers really have no need for the target url or to log at all.

Copy link
Member

Choose a reason for hiding this comment

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

I would be in favor of having io.deephaven.server.runner.DeephavenApiServer#run be in control of the necessary logging and removing targetUrl from initialize. This gives us a chance to delay logging until after the server has started, and be smarter about when and what we actually log. This suggests we may need to use reflection (or some "hacky" means for PSK), or elevate PSK to the authentication project.

@niloc132
Copy link
Member Author

Possible layout for docker compose sample.

fcfb5f7

I think this is too much, even without making it clearer. The only part I want to keep (in a different commit...) is to create the /apps/libs directory when we make the images, rather than make users create it themselves - but the steps to create this are a bit too complex, unless we intend to publish the image. I think it would be better to have an explicit Dockerfile example somewhere, rather than a runnable dev docker example.

@devinrsmith
Copy link
Member

Do we publish these at all? that is, JAVA_PUBLIC vs JAVA_BASIC?

I see value in publishing PSK, but it's hard to see immediate value from publishing the other ones.

authType strings - okay to default to the classname for dh-provided ones, easier to user from a client (rather than guessing from the AuthHandlers value

classname sounds good

futher parameterize the oidc provider to actually be generic for any oidc provider (this implies testing with other providers), or just be explicit that this is just a 'keycloak' provider?

I'd say "no" to further parameterization at this point. May be reasonable to rename to "oidc-keycloak" or similar.

@niloc132
Copy link
Member Author

Out of the box, mtls, psk, and keycloak are configurable for "just limit users to those who fit this simple criteria", which solves the major feature gap we were missing here. With oidc/keycloak, an existing instance can be used by just adding a new realm or new client an an existing realm to grant/deny access, and with mtls controlling the CA used for browser or programmatic clients is at least enough to offer some guarantee over who can connect or not.

The Basic sql one is a little weaker here (though you can give it an existing postgresql database and specify the table/columns to use). It might be a stretch here, but it seems odd to skip this one, as it certainly can work out of the box (and doubles as its own tool to create passwords for the DB to store) in a simpler way than to say "either roll your own or also stand up Keycloak).

@niloc132 niloc132 marked this pull request as ready for review February 17, 2023 18:45
nbauernfeind
nbauernfeind previously approved these changes Feb 21, 2023
@niloc132 niloc132 merged commit 8c92bff into deephaven:main Mar 2, 2023
@github-actions github-actions bot locked and limited conversation to collaborators Mar 2, 2023
@deephaven-internal
Copy link
Contributor

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
documentation Improvements or additions to documentation DocumentationNeeded ReleaseNotesNeeded Release notes are needed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Enable anonymous authentication warning EPIC: Authentication support
4 participants