Re: [PoC] Federated Authn/z with OAUTHBEARER
Jacob Champion <jacob.champion@enterprisedb.com>
Commits
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
meson: Fix install-quiet after clean
- a9ffb35274fb 18.0 landed
- 4ae03be54734 19 (unreleased) landed
-
oauth: Run Autoconf tests with correct compiler flags
- 3d23f68c5529 18.0 landed
- 990571a08b66 19 (unreleased) landed
-
Link libpq with libdl if the platform needs that.
- 4df477153a6b 19 (unreleased) landed
- 7bd752c1fb8e 18.0 landed
-
Doc: correct spelling of meson switch.
- 3faac9d14063 16.9 landed
- 766d2e673342 17.5 landed
- ac557793d478 18.0 landed
-
oauth: Correct SSL dependency for libpq-oauth.a
- 3db68212a393 18.0 landed
-
oauth: Fix Autoconf build on macOS
- 4ea1254f35b2 18.0 cited
-
oauth: Move the builtin flow into a separate module
- b0635bfda053 18.0 landed
-
Remove a stray "pgrminclude" annotation
- 764d501d24ba 18.0 cited
-
oauth: Simplify copy of PGoauthBearerRequest
- 1cf4c56480f8 18.0 landed
-
oauth: Improve validator docs on interruptibility
- 873c0fd67872 18.0 landed
-
oauth: Disallow synchronous DNS in libcurl
- d7e40845f923 18.0 landed
-
oauth: Fix postcondition for set_timer on macOS
- 434dbf6907ec 18.0 landed
-
oauth: Use IPv4-only issuer in oauth_validator tests
- 8d9d5843b55f 18.0 landed
-
Work around OAuth/EVFILT_TIMER quirk on NetBSD.
- c301a0a74a8a 18.0 landed
-
oauth: Fix incorrect const markers in struct
- 03366b61dfe5 18.0 landed
-
Add missing entry to oauth_validator test .gitignore
- 2c53dec7f440 18.0 landed
-
cirrus: Temporarily fix libcurl link error
- 9d9a71002a1c 18.0 landed
-
Add support for OAUTHBEARER SASL mechanism
- b3f0be788afc 18.0 landed
-
libpq: Handle asynchronous actions during SASL
- a99a32e43ed7 18.0 landed
-
require_auth: prepare for multiple SASL mechanisms
- f8d8581ed882 18.0 landed
-
Move PG_MAX_AUTH_TOKEN_LENGTH to libpq/auth.h
- e21d6f297158 18.0 landed
-
Make SASL max message length configurable
- 6d16f9debae0 18.0 landed
-
jsonapi: fully initialize dummy lexer
- 41b023946dfd 18.0 landed
-
common/jsonapi: support libpq as a client
- 0785d1b8b2fa 18.0 landed
-
Remove fe_memutils from libpgcommon_shlib
- f1976df5eaf2 18.0 landed
-
Revert ECPG's use of pnstrdup()
- f0096ef13be2 13.17 landed
- 3557185538fe 14.14 landed
- 2de129b356bf 15.9 landed
- ee2997c678d8 16.5 landed
- e9e05c655069 17.0 landed
- 5388216f6adc 18.0 landed
-
Explicitly require password for SCRAM exchange
- adcdb2c8dda4 17.0 landed
-
Refactor SASL exchange to return tri-state status
- 24178e235ea5 17.0 landed
On Thu, Mar 28, 2024 at 3:34 PM Daniel Gustafsson <daniel@yesql.se> wrote:
> In jsonapi.c, makeJsonLexContextCstringLen initialize a JsonLexContext with
> palloc0 which would need to be ported over to use ALLOC for frontend code.
Seems reasonable (but see below, too).
> On
> that note, the errorhandling in parse_oauth_json() for content-type checks
> attempts to free the JsonLexContext even before it has been created. Here we
> can just return false.
Agreed. They're zero-initialized, so freeJsonLexContext() is safe
IIUC, but it's clearer not to call the free function at all.
But for these additions:
> - makeJsonLexContextCstringLen(&lex, resp->data, resp->len, PG_UTF8, true);
> + if (!makeJsonLexContextCstringLen(&lex, resp->data, resp->len, PG_UTF8, true))
> + {
> + actx_error(actx, "out of memory");
> + return false;
> + }
...since we're using the stack-based API as opposed to the heap-based
API, they shouldn't be possible to hit. Any failures in createStrVal()
are deferred to parse time on purpose.
> - echo 'libpq must not be calling any function which invokes exit'; exit 1; \
> + echo 'libpq must not be calling any function which invokes exit'; \
> The offending codepath in libcurl was in the NTLM_WB module, a very old and
> obscure form of NTLM support which was replaced (yet remained in the tree) a
> long time ago by a full NTLM implementatin. Based on the findings in this
> thread it was deprecated with a removal date set to April 2024 [0]. A bug in
> the 8.4.0 release however disconnected NTLM_WB from the build and given the
> lack of complaints it was decided to leave as is, so we can base our libcurl
> requirements on 8.4.0 while keeping the exit() check intact.
Of the Cirrus machines, it looks like only FreeBSD has a new enough
libcurl for that. Ubuntu won't until 24.04, Debian Bookworm doesn't
have it unless you're running backports, RHEL 9 is still on 7.x... I
think requiring libcurl 8 is effectively saying no one will be able to
use this for a long time. Is there an alternative?
> + else if (strcasecmp(content_type, "application/json") != 0)
> This needs to handle parameters as well since it will now fail if the charset
> parameter is appended (which undoubtedly will be pretty common). The easiest
> way is probably to just verify the mediatype and skip the parameters since we
> know it can only be charset?
Good catch. application/json no longer defines charsets officially
[1], so I think we should be able to just ignore them. The new
strncasecmp needs to handle a spurious prefix, too; I have that on my
TODO list.
> + /* TODO investigate using conn->Pfdebug and CURLOPT_DEBUGFUNCTION here */
> + CHECK_SETOPT(actx, CURLOPT_VERBOSE, 1L, return false);
> + CHECK_SETOPT(actx, CURLOPT_ERRORBUFFER, actx->curl_err, return false);
> CURLOPT_ERRORBUFFER is the old and finicky way of extracting error messages, we
> should absolutely move to using CURLOPT_DEBUGFUNCTION instead.
This new way doesn't do the same thing. Here's a sample error:
connection to server at "127.0.0.1", port 56619 failed: failed to
fetch OpenID discovery document: Weird server reply ( Trying
127.0.0.1:36647...
Connected to localhost (127.0.0.1) port 36647 (#0)
Mark bundle as not supporting multiuse
HTTP 1.0, assume close after body
Invalid Content-Length: value
Closing connection 0
)
IMO that's too much noise. Prior to the change, the same error would have been
connection to server at "127.0.0.1", port 56619 failed: failed to
fetch OpenID discovery document: Weird server reply (Invalid
Content-Length: value)
The error buffer is finicky for sure, but it's also a generic one-line
explanation of what went wrong... Is there an alternative API for that
I'm missing?
> + /* && response_code != 401 TODO */ )
> Why is this marked with a TODO, do you remember?
Yeah -- I have a feeling that 401s coming back are going to need more
helpful hints to the user, since it implies that libpq itself hasn't
authenticated correctly as opposed to some user-related auth failure.
I was hoping to find some sample behaviors in the wild and record
those into the suite.
> + print("# OAuth provider (PID $pid) is listening on port $port\n");
> Code running under Test::More need to use diag() for printing non-test output
> like this.
Ah, thanks.
> +#if LIBCURL_VERSION_MAJOR <= 8 && LIBCURL_VERSION_MINOR < 4
I don't think this catches versions like 7.76, does it? Maybe
`LIBCURL_VERSION_MAJOR < 8 || (LIBCURL_VERSION_MAJOR == 8 &&
LIBCURL_VERSION_MINOR < 4)`, or else `LIBCURL_VERSION_NUM < 0x080400`?
> my $pid = open(my $read_fh, "-|", $ENV{PYTHON}, "t/oauth_server.py")
> - // die "failed to start OAuth server: $!";
> + or die "failed to start OAuth server: $!";
>
> - read($read_fh, $port, 7) // die "failed to read port number: $!";
> + read($read_fh, $port, 7) or die "failed to read port number: $!";
The first hunk here looks good (thanks for the catch!) but I think the
second is not correct behavior. $! doesn't get set unless undef is
returned, if I'm reading the docs correctly. Yay Perl.
> + /* Sanity check the previous operation */
> + if (actx->running != 1)
> + {
> + actx_error(actx, "failed to queue HTTP request");
> + return false;
> + }
`running` can be set to zero on success, too. I'm having trouble
forcing that code path in a test so far, but we're going to have to do
something special in that case.
> Another issue I have is the sheer size and the fact that so much code is
> replaced by subsequent commits, so I took the liberty to squash some of this
> down into something less daunting. The attached v22 retains the 0001 and then
> condenses the rest into two commits for frontent and backend parts.
Looks good.
> I did drop
> the Python pytest patch since I feel that it's unlikely to go in from this
> thread (adding pytest seems worthy of its own thread and discussion), and the
> weight of it makes this seem scarier than it is.
Until its coverage gets ported over, can we keep it as a `DO NOT
MERGE` patch? Otherwise there's not much to run in Cirrus.
> The final patch contains fixes for all of the above review comments as well as
> a some refactoring, smaller clean-ups and TODO fixing. If these fixes are
> accepted I'll incorporate them into the two commits.
>
> Next I intend to work on writing documentation for this.
Awesome, thank you! I will start adding coverage to the new code paths.
--Jacob
[1] https://datatracker.ietf.org/doc/html/rfc7159#section-11