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

feat: Make headers to add to request in openid-connect plugin configurable. #2903

Merged
merged 112 commits into from
Jan 5, 2021

Conversation

jenskeiner
Copy link
Contributor

@jenskeiner jenskeiner commented Nov 30, 2020

What this PR does / why we need it:

See #2880. The current implementation may lead to very large headers, thus bloating the request size to downstream substantially. This can be an issue by itself but is also not desired if the different tokens (access, ID, user info) largely contain the same information. Also, instead of using the X-Access-Token header it may be more appropriate to use the Authorization header for the access token, so that downstream plugins or services can extract it correctly; see e.g. authz-keycloak plugin. Finally, the plugin was so far only expecting an incoming access token in the Authorization header, but not the X-Access-Token header, which seemed somewhat inconsistent.

I've added options that can be configured for the openid-connect plugin that allow to control which headers get added and which header specifically shall be used for the access token. The defaults reproduce the current behavior. The plugin will also look in the X-Access-Token header, in addition to the Authorization header, for an access token in an incoming request.

Pre-submission checklist:

  • Did you explain what problem does this PR solve? Or what new features have been added?
  • Have you added corresponding test cases?
    Haven't added any new cases for now. The headers set by the plugin were not tested, as far as I can tell. It may be a good idea to think about some new tests around here. That would likely require a bit of legwork to set up a dummy OIDC Identity Provider with all needed endpoints to go through all possible code paths though.
  • Have you modified the corresponding document?
    I understand that the plugin documentation should be updated, but I want to get some feedback on the code changes first. Happy to adjust the documentation if the code changes get signed off.
  • Is this PR backward compatible? If it is not backward compatible, please discuss on the mailing list first

@jenskeiner jenskeiner changed the title feat: Make headers to add to request configurable. feat: Make headers to add to request in openid-connect plugin configurable. Nov 30, 2020
apisix/plugins/openid-connect.lua Outdated Show resolved Hide resolved
@@ -152,26 +225,28 @@ function _M.access(plugin_conf, ctx)
core.log.error("failed to introspect in openidc: ", err)
return response
end
if response then
Copy link
Member

Choose a reason for hiding this comment

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

Why remove this part?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason is that adding the user info header is now handled in the introspect method. The header will still be added, if so configured, and introspection is done via the actual endpoint as opposed to just checking against the public key. Let me know if you see any problem with this approach.

apisix/plugins/openid-connect.lua Outdated Show resolved Hide resolved
apisix/plugins/openid-connect.lua Show resolved Hide resolved
apisix/plugins/openid-connect.lua Outdated Show resolved Hide resolved
@@ -40,7 +41,11 @@ local schema = {
logout_path = {type = "string"}, -- default is /logout
redirect_uri = {type = "string"}, -- default is ngx.var.request_uri
public_key = {type = "string"},
token_signing_alg_values_expected = {type = "string"}
token_signing_alg_values_expected = {type = "string"},
set_access_token_header = {type = "boolean"}, -- default is true
Copy link
Member

Choose a reason for hiding this comment

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

Let's use the new way to set the default value:

default = "*"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, hadn't seen it before. Will change.

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've changed the scheme for the options that I've added. But have left the existing options untouched to not make this PR any larger.

@spacewander
Copy link
Member

Please write new tests to cover your change, and make sure they are passed.

@jenskeiner
Copy link
Contributor Author

Please write new tests to cover your change, and make sure they are passed.

I'm on it. I had a look at the existing structure and I'm relatively confident I can add more test cases to verify the setting of the respective headers.

However, one thing is still unclear to me: There are cases already where token introspection is performed against a configured identity provider endpoint. The cases were added by @sshniro here: 14f979e

Can anyone point me to the place where this introspection endpoint is set up for the tests? I can't find the corresponding code inside the project.

I may need to add more dummy endpoints to verify the authorization flow in case a bearer token is not present in the incoming request and I want to make that consistent with the existing dummy introspection endpoint.

-- Token is valid.

-- Add configured access token header, maybe.
add_access_token_header(ctx, conf, token)
Copy link
Member

Choose a reason for hiding this comment

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

Token is invalid if has_token is false but conf.bearer_only is true

end

-- Add configured access token header, maybe.
add_access_token_header(ctx, conf, token)
Copy link
Member

Choose a reason for hiding this comment

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

Ditto

@sshniro
Copy link
Member

sshniro commented Dec 2, 2020

Hi @jenskeiner for your question on the introspection endpoint test case, I have used the Keycloak to test this scenario.

The code link to setup Keycloak:

docker run --rm -itd -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=123456 -p 8090:8080 -p 8443:8443 sshniro/keycloak-apisix

Test cases written for the introspection with Keyclaok:
=== TEST 11: Update route with keycloak config for introspection

@membphis the docker image is using my personal docker repo, if needed I can migrate it to an official APISIX docker repository.

@membphis
Copy link
Member

membphis commented Dec 2, 2020

@membphis the docker image is using my personal docker repo, if needed I can migrate it to an official APISIX docker repository.

seems good for me.

what do you think? @moonming

@moonming
Copy link
Member

moonming commented Dec 3, 2020

@membphis the docker image is using my personal docker repo, if needed I can migrate it to an official APISIX docker repository.

seems good for me.

what do you think? @moonming

we can add keycloak-apisix docker to CI, and I am not sure if it is appropriate to migrate it to an official APISIX docker repository, most of APISIX users don't need keycloak.

@@ -112,7 +112,7 @@ done
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
"127.0.0.1:8888": 1
Copy link
Member

Choose a reason for hiding this comment

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

8888, Is it a valid "openid" port?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hey, I'm trying to add tests to check if the plugin sets the headers correctly as configured. I don't have a local development setup yet, so have to run the checks on this PR on GitHub to see if it's working.

I switched the upstream port to 8888 since that's where a mendhak/http-https-echo container is running. This one returns in the response body the full content of the incoming request as a JSON string. So I think I can check if the plugin has set the headers correctly by checking the response body. If the tests still work with the new port, I will try to add a case to do some actual verification of the headers.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, thanks for the link. It's more that I have to set up a fresh Linux VM or similar since I want that separate from the rest.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Understood, seems to be the better option. Will adjust the code.

@jenskeiner
Copy link
Contributor Author

@spacewander @membphis @moonming Sorry for the noise, but took a while to set up test cases that go through the full OIDC Relying Party flow which can be used when the incoming request doesn't contain a bearer token yet.

Please give this another good review and let me know any issues. Thanks.

I know, I still need to update the documentation, but will wait on feedback regarding code changes first.

@jenskeiner
Copy link
Contributor Author

The code changes in this PR are basically just to solve some issues I was seeing when combining this plugin with the auth-keycloak one. I wanted to make sure that the latter can extract the access token from the request, even if it is contained in a session cookie or potentially some other server-side storage as supported by the openidc module. The changes make sure that the token can always be added in a header where authz-keycloak expects it. I also wanted to reduce some noise from the ID token and the user info that were getting added in separate headers because you probably don't need those always and they make the HTTP header even larger.

Generally, I think the interplay between this plugin and auth-keycloak should be re-considered. Messing with the request headers could be avoided completely if auth-keycloak could leverage the openidc module to extract the access token from the request (from a header or cookie or...) itself using a method that is currently internal to that module. Then, it wouldn't need to rely on the token being stored in a header and code to extract the token could be removed in favor of using the openidc module's code for that.

I'm just wondering what anyone's thoughts on this are.

The current solution with the headers works for me and I'll look at authz-keycloak next to start the work on lazy path loading from Keycloak. But longer term, would be better to clean up the code some more and not rely on the openid-connect plugin to fix up the headers for the auth-keycloak plugin. Thoughts and feedback welcome.

@sshniro Maybe you have thoughts on this as well...

t/plugin/openid-connect.t Outdated Show resolved Hide resolved
apisix/plugins/openid-connect.lua Show resolved Hide resolved
apisix/plugins/openid-connect.lua Outdated Show resolved Hide resolved
@spacewander spacewander added this to the 2.3 milestone Dec 27, 2020
@spacewander
Copy link
Member

Fantasy! Before we can merge it, @jenskeiner please write some English docs.

@jenskeiner
Copy link
Contributor Author

Fantasy! Before we can merge it, @jenskeiner please write some English docs.

Yep, will do, may take until next Monday though. In the meantime, let me know any worries.

 Bitte geben Sie eine Commit-Beschreibung für Ihre Änderungen ein. Zeilen,
@jenskeiner
Copy link
Contributor Author

@spacewander Updated the documentation now as well. Can you give it another good review to ensure it's consistent and clear enough?

@spacewander spacewander merged commit 67df073 into apache:master Jan 5, 2021
@spacewander
Copy link
Member

@jenskeiner
Merged. Thanks!

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.

6 participants