Skip to content

Jwt_authn: first draft of Http filter implementation.#3339

Merged
htuch merged 33 commits intoenvoyproxy:masterfrom
qiwzhang:jwt_authn_all0
Jun 14, 2018
Merged

Jwt_authn: first draft of Http filter implementation.#3339
htuch merged 33 commits intoenvoyproxy:masterfrom
qiwzhang:jwt_authn_all0

Conversation

@qiwzhang
Copy link
Contributor

Signed-off-by: Wayne Zhang qiwzhang@google.com

Description:
This is first draft of porting jwt authentication filter from istio/proxy to Envoy.

Risk Level: Low

@qiwzhang
Copy link
Contributor Author

@lizan @htuch Please help to review this design.

Copy link
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

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

Some initial comments, nice to see this coming together.

Copy link
Member

Choose a reason for hiding this comment

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

Can you import (or export) these under some prefixed path?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

This seems a general HTTP or core utility to have in Envoy.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Is there an RFC for canonical URI parsing to reference?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added RFC

Copy link
Member

Choose a reason for hiding this comment

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

Is the assumption here that the cluster will be configured for TLS? I'd be interesting in seeing some way to validate or warn if this isn't the case, since it would be a pretty bad misconfiguration to fetch certs or public keys without TLS.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Normally, public key servers only support TLS. Users have to config cluster with TLS.

Copy link
Member

Choose a reason for hiding this comment

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

I'm OK with saying this, but I think it would make sense to document with a strong warning here. It would be pretty scary to get this one wrong.

Copy link
Member

Choose a reason for hiding this comment

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

Is the cache only for preconfigured keys or is there an ability to dynamically add to it? Please clarify in comments, thanks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added comment

@qiwzhang qiwzhang changed the title Add jwt_authn filter implementation Jwt_authn: first draft of Http filter implementation. May 11, 2018
@qiwzhang qiwzhang force-pushed the jwt_authn_all0 branch 3 times, most recently from 9cb883e to e60c317 Compare May 16, 2018 16:14
Copy link
Member

Choose a reason for hiding this comment

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

Nit: any chance we can drop the include/ part of this? It's just aesthetics, but yeah..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Nit: make these temporary variables const.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Just a thought; would a regex possibly be clearer?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think regex is much slower than string.find()

Copy link
Member

Choose a reason for hiding this comment

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

Maybe; but if you precompile the regex it could be fairly reasonable, see the ongoing discussion in #3269 (comment).

Copy link
Member

Choose a reason for hiding this comment

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

Needs a bunch of test covering expected and corner cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added test

Copy link
Member

Choose a reason for hiding this comment

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

tokens.empty()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

TODO(qiwzhang):

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Nit: const

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Given the above cache check is only for pre-configured JWKS, should we have a TODO here to add a cache for the fetchRemoteJwks() result? If we have two back-to-back requests for the same remote JWKS, I imagine we don't want to make two HTTP calls?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added TODO

Copy link
Member

Choose a reason for hiding this comment

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

I'm OK with saying this, but I think it would make sense to document with a strong warning here. It would be pretty scary to get this one wrong.

Copy link
Member

Choose a reason for hiding this comment

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

Nit: const

Copy link
Member

Choose a reason for hiding this comment

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

Nit: You can bring std::string body into here and make it a const std::string

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

@saumoh this is similar to what you do in ext_authz. Is there a bug here or is this a simpler way of looking at the side-call state?

Copy link
Member

Choose a reason for hiding this comment

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

Envoy doesn't allow static non-POD (like Google3), consider using ConstSingleton or the like.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Prefer constexpr and Envoy style naming such as PubKeyCacheExpirationSec for constant scalars.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Who is a qualified person to review this JWKS specific logic?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is NOT the JWKS specific. This is just our implementation based on the experience from Google cloud endpoint.
Updated the comment.

Copy link
Member

Choose a reason for hiding this comment

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

Nit: please use explicit sized types, e.g. uint32_t as per Envoy style, or if you're being compatible with an existing API, something like size_t as determined by the STL API.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Can you confirm that you've verified full test coverage on the new code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, I did not have full test converge for the new code yet. I am still adding new tests into this PR.

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 you should push almost all of this code to jwt_verifier_lib, it doesn't have to belong to Envoy code base. The only dependency seems to be the configuration, but it is only audiences (vector) and jwks (string) so you should be able to pass by parameter.

Copy link
Member

Choose a reason for hiding this comment

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

ping

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about it. It is NOT just a pure cache. It is a cached Jwks + issuer_config. For each request, we need to access per-issuer config, such as payload_header, and we also need to access cached Jwks. We only need to do one map lookup by issuer if we combine jwks with issuer_config. That is the current JwksCache design.

If we split into a separate pure JwksCache, then, I agree with you, it can be moved into Jwt_verify_lib.
Current design is try to reduce a map lookup by issuer.

Copy link
Member

Choose a reason for hiding this comment

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

issue config is just audience + jwks, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

also "forward_jwt" and "payload_header".
On second throught, I may go with your suggestion, it is cleaner. I will create a config_map to lookup config by issuer, and jwks_cache. If we use unorderred_map, lookup is O(1), it is OK to lookup twice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried, but decided to keep the current shape as: with new JwtAuthn config change (#3381), audience checking may not be in JwksCache object. Further re-factory is needed to support the config change.

Copy link
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

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

Another round of feedback, looking like it's in good shape @qiwzhang. Can you ping me on this PR when you're done with this feedback and you have full test coverage? Thanks.

Copy link
Member

Choose a reason for hiding this comment

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

You probably want to test more corner cases, e.g. scheme://adf-scheme://adf, :// or /:/adsfetc. How do you handle invalid URIs as user input?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added some corner cases. Also updated the header file to say this function will NOT validate URI.

Copy link
Member

Choose a reason for hiding this comment

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

Should this be bool okToBypass() const; ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Nit:s/jwks/JWKS/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

How is in-progress fetching possible given we only get here via decodeHeaders? Should this just be an ASSERT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated the comment

Copy link
Member

Choose a reason for hiding this comment

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

Nit: const

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Nit: != nullptr

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Nit: std::make_unique

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Nit: Unnecessary blank line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

Copy link
Member

Choose a reason for hiding this comment

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

Nit: Be consistent in using : as a separator between these lines.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Is there a better name than factory here? E.g. ThreadLocalDataStore?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Rename it to Config

@qiwzhang qiwzhang force-pushed the jwt_authn_all0 branch 5 times, most recently from a343c49 to f949bdf Compare May 25, 2018 19:23
Copy link
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

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

Great, I think this is looking pretty good now, I would be interesting in @saumoh take.

Copy link
Member

Choose a reason for hiding this comment

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

What about URIs with ports in them, e.g. http://foo.com:1234/foo? I think in general URIs get pretty complicated, for example you can encode usernames and passwords in them. This is kind of why I was in favor of having some standard library for this. If the limitations here are acceptable for your needs, that's fine, just document them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

returned host and path are used for message->headers() Host() and Path() field for http async client->Send() call. It is OK for host to have port. Updated comment for it

Copy link
Member

Choose a reason for hiding this comment

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

Nit: remove gratuitous blank line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

@saumoh can you comment on how this relates to the state machine and handling done for ext authz?

Copy link
Member

Choose a reason for hiding this comment

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

Is this RBAC?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, copy & paste error. changed.

Copy link
Member

Choose a reason for hiding this comment

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

Probably cleaner to work with proto DurationUtil, e.g. DurationUtil::durationToMilliseconds.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Please add a one-liner // This test validates foo and bar. above each TEST_F.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

Can you recommend someone who can review the JWT business logic in these tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually these two tests are covered by jwt_verify_lib. So remove them here.

Copy link
Member

Choose a reason for hiding this comment

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

I can't spot this on the CI coverage report (https://53860-65214191-gh.circle-artifacts.com/0/build/envoy/generated/coverage/coverage.html), do you know why?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Your coverage maybe from other PR coverage tests. I don't know how to check show coverage from PR coverage test. But from the test log it has jwks_cache.html which should reflect jwks_cache code coverage.

@qiwzhang
Copy link
Contributor Author

@htuch please hold for another round of review. I am going to add filter_test and integration_test. After that will ping you for another round of review.

@qiwzhang qiwzhang force-pushed the jwt_authn_all0 branch 2 times, most recently from fdc2506 to 7585069 Compare June 7, 2018 23:00
@qiwzhang
Copy link
Contributor Author

qiwzhang commented Jun 8, 2018

@lizan @htuch This PR is ready for review. It only misses documentation which will be added as a separate PR. PTAL

Copy link
Member

Choose a reason for hiding this comment

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

Use HeaderMapImpl::appendToHeader?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, this is not my change. I may "rebase" wrong change. Will fix it.

Copy link
Member

Choose a reason for hiding this comment

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

can we make this part an utility function for HttpUri, similar to Grpc::Common::prepareHeaders.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

Choose a reason for hiding this comment

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

ping

@htuch
Copy link
Member

htuch commented Jun 12, 2018

@qiwzhang I'm taking another look. FYI for the future, it would be super helpful to reviewers if you avoided doing forced pushes, which it looks like you have been doing, since it's hard to spot the delta since the last round of comments.

qiwzhang added 25 commits June 14, 2018 19:02
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
@htuch htuch merged commit 7ca1882 into envoyproxy:master Jun 14, 2018
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

Successfully merging this pull request may close these issues.

3 participants