F21 wrote:
Just realized the proposed design does not take into account the
situation where someone wants to do HTTP Basic or Digest auth against
avatica, and then pass a separate username/password pair to the backing db.
On 17/05/2017 9:24 AM, F21 wrote:
Hi Josh,
Thanks for the detailed response!
In terms of the HashLoginService in Jetty, does the client need to
pass a role to Avatica? If so, how is this done?
Also, if I define multiple roles for a given user in Avatica using a
properties file, do they have any actual effect (from what I can see,
Avatica does not seem to use the roles at all)?
No, the user doesn't have a concept of roles. The roles are a
Jetty-construct to map a set of users that are logically grouped
together. e.g. an "admin" role may be authorized to see resources that a
"developers" role may not be authorized.
Here's my summary of the possible authentication methods (let me know
if I miss something):
- HTTP Basic or HTTP Digest authentication against Avatica.
- SPNEGO against Avatica.
- SPNEGO against Avatica that is impersonated, so that the queries
against the database are run against the authenticated user.
- A user and password pair (`user` and `password` keys in the
OpenConnectionRequest map) that is passed straight down to the backing
database (Avatica does not do any authentication).
I am still somewhat confused about using `avatica_user` and
`avatica_password` for HTTP Basic and Digest auth. I am assuming that
instead of passing those as a HTTP header (Authorization: Basic
QWxhZGRpbjpPcGVuU2VzYW1l), I should
set them in the map for OpenConnectionRequest and set the
`authentication` key to either `BASIC` or `DIGEST`?
No: avatica_user and avatica_password are implementation details for the
Avatica JDBC driver only. *Each driver* needs to implement hooks for how
the HTTP authentication is implemented (if you choose to do so).
Specifically, you would need to expose configuration in the Go driver to
accept username and password to specifically use for the HTTP requests.
It's certainly a reasonable idea to re-use the same naming as it keeps
things concise across client implementations :)
Is passing the user and password pair using `user` and `password`
straight down to the backing database an officially supported method?
That's what was requested with the Go driver. In this instance, they
are using the Phoenix Query Server, but I believe the PQS was modified
to check username/password pairs, as I don't believe Phoenix/HBase
supports username/password auth.
Ok, I think I see where your confusion is coming from.
"user" and "password" are officially supported as they are constructs
from JDBC. There is the implicit assumption that these are present
(unless your "real backend database" doesn't support them).
Avatica layers more authentication on "top" of that. The big difference
is that HTTP Basic/Digest authentication and SPNEGO authentication are
done at the "protocol" level. The protocol authentication we're getting
isn't directly translated into backend Avatica RPCs.
Implementations of Avatica *can* make an exception to that rule. For
example, with Apache Phoenix, we hook into the authenticated user from
the SPNEGO request and Avatica "impersonates" the end-user (as we know
that the request was strongly authenticated with their Kerberos
credentials already). Phoenix/HBase uses that impersonated Kerberos user
_in lieu_ of the normal JDBC "user" and "password".
Does this make sense? The core is that we have two separate layers of
authentication but you can wire them together in the backend.
For SPNEGO impersonation, is the request automatically impersonated as
long as the backing db has implemented impersonation?
No. In the server-side portion of a SPNEGO handshake, the server *never*
sees the client's "keys". It's impossible for Avatica to perform some
action as the end user.
How this can work is that Avatica is configured to use its server
identity but *say that it is the client*. e.g. "I am <client> here are
my credentials: <server_keys>". The reason this can work is if the
backend DB is configured to allow the <server> to be treated as
<client>. In any other case, this is a security flaw.
In terms of implementation, my current difficulties are:
- Lack of a good pure-go SPNEGO library: Maybe it's possible to
support SPNEGO on *nix-like systems only, while throwing an error if
SPNEGO is used on Windows.
That's a shame. Maybe one will come about if we wait :)
- The need to force all the config into the DSN string (developer
experience). For example, a possible implementation is:
http://username:password@address:port/schema -> pass the username and
password straight through to the backing db
http://username:password@address:port/schema?authentication=BASIC ->
use the username and password for HTTP BASIC auth
http://username:password@address:port/schema?authentication=DIGEST ->
use the username and password for HTTP DIGEST auth
http://address:port/schema?authentication=SPNEGO&principal=some-principal&keytab=/path/to/some/keytab
-> use SPNEGO
In the case of using a username + password, if the authentication
parameter is unintentionally added or omitted, it might not be
apparent why things are not working. I would love to hear what you
guys think about this design.
I'd avoid tying the "wire" authentication to the "database"
authentication (as alluded to above). For example, if there is some
authentication proxy sitting between client and Avatica server, you may
need to provide two different sets of credentials (the wire creds to get
through the proxy, and then the real database creds). The former is
purely at the HTTP level, the latter would be included in the
OpenConnectionRequest.
Hopefully this clarifies some things. Let me know if there is still some
confusion. This is admittedly a little weird, but I believe it's
(presently) what we want.