traefik-plugins / traefik-jwt-plugin Goto Github PK
View Code? Open in Web Editor NEWTraefik plugin which checks JWT tokens for required fields. Supports Open Policy Agent (OPA) and signature validation with JWKS
License: Apache License 2.0
Traefik plugin which checks JWT tokens for required fields. Supports Open Policy Agent (OPA) and signature validation with JWKS
License: Apache License 2.0
I am testing this plugin for validating jwt with treaffik but it looks like opa is needed even if I only want to validate the jwt.
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: jwt
spec:
plugin:
jwt:
Required: true
Keys:
- https://authz.authentication.svc:8080/keys
if I don't set the OpaUrl the request is always authorized regardless of whether the Authorization
header is set. If I set the OpaUrl, opa is called regardless of the Authorization
header.
I am using v0.0.11
Update the version in the README.md file (currently 0.0.11).
It took me a while to understand why some of the feature didn't work, before updating the version thanks to #43
@blagerweij could you please release a new version ? because current 0.0.7 is almost a year old and there were multiple commits/fixes already
The plugin was not imported into Traefik Plugin Catalog.
Cause:
failed to create temp GOPATH: mkdir /tmp/traefik-plugin-gop3634429944: read-only file system
Traefik Plugin Analyzer will restart when you will close this issue.
If you believe there is a problem with the Analyzer or this issue is the result of a false positive, please contact us.
If it does, how can I configure? Or if it doesn't, how can I implement it? Please advise.
Hello,
I want return JWT data in headers with JwtHeaders option and the value of header is hexa structure instead of real value.
My config is :
JwtHeaders:
Subject: sub
User: preferred_username
Header returned is :
GET / HTTP/1.1
Host: whoami.xxxxxxxx.net
User-Agent: PostmanRuntime/7.26.10
Accept: */*
Accept-Encoding: gzip, deflate, br
Authorization: Bearer xxxxxxxxxxxxxxxxxxxxxxxxx
Subject: {0xc000495400 {0x2cbc100 0xc0008c1a90 152}}
User: {0xc000495400 {0x2cbc100 0xc0008c1c20 152}}
X-Forwarded-For: xx.xx.xx.xx
X-Forwarded-Host: whoami.xxxxxxxx.net
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: traefik-5b747b5845-mlw7r
X-Real-Ip: xx.xx.xx.xx
My JWT token is here JWT.IO SHARE
It's a bug ? or no ?
Thx
Hello
Currently plugin tries to parse request body to pass corresponding info to OPA.
The 1st issue is that this is not mentioned anywhere in docs, so I suggest a corresponding section should be added to README.
2nd issue is that body parsing is quite resource consuming operation and may introduce latencies that are not desirable in high volume application. So I have another proposal to introduce a flag that can completely disable parsing of request body. If you're interested I am willing to provide a PR.
Using the code provided in the README with helm installation gave me the following error:
ERROR: plugin enabled is missing moduleName/version keys !
Even if I use the latest version (i.e. v0.7.0).
However, it works like a charm when I use the code provided in the Pluging catalog:
https://plugins.traefik.io/plugins/62947304108ecc83915d7782/jwt-access-policy
Is this behavior expected or I'm missing something?
I read the code and right now middleware only supports parsing JWT token from header.
I am looking for support of parsing token from cookie as well. Are you planning to add support for that in future?
I can try to add support and raise a PR.
Hello @blagerweij
I am maintaining few more Traefik plugins and recently decided to try organizing a central place for them and maybe in future other plugins to
For this purpose I've created a new GH organization
where I plan to move current plugins I am currently maintaining.
https://github.com/traefik-plugins
So I'd like to ask if you mind to donate \ move this plugin to that organization as well ?
I could give you all required permission to keep maintaing the code.
WDYT ? Thnx in advance.
The plugin was not imported into Traefik Pilot.
Cause:
failed to get the latest tag: missing tag/version
Traefik Plugin Analyzer will restart when you will close this issue.
If you believe there is a problem with the Analyzer or this issue is the result of a false positive, please contact us.
Could it be that the "application/x-www-url-formencoded" should be "application/x-www-form-urlencoded" ?
line 676 in jwt.go :
} else if contentType == "application/x-www-url-formencoded" {
When sending a form using Insomnia the form data is not included in the request sent to OPA
Hello,
I’m using the plugin to validate a jwt token with traefik, I’m not using OPA. However when I’m doing a request from the front-end, the preflight request generated by the browser is failing with an error 403 "authorization header missing" because there are no Authorization header in a preflight request. What can I do to solve that problem ?
Thanks for providing this plugin... I think this is probably my issues, but wondering if I could get some help.
I'm trying to set the JWT auth up in Traefik while using docker-compose (I've copied the relevant sections from my compose file below)
I'm struggling converting the dynamic configuration into the labels format.
version: '3'
services:
traefik:
image: traefik:latest
command:
- --providers.docker
- --api
- --log.level=DEBUG
...
- --experimental.plugins.traefik-jwt-plugin.moduleName=github.com/team-carepay/traefik-jwt-plugin
- --experimental.plugins.traefik-jwt-plugin.version=v0.6.0
I've tried several variations of the below to align with the examples provided and I get an error that the configuration is invalid.
labels:
- traefik.http.middlewares.my-traefik-jwt-plugin.plugin.traefik-jwt-plugin.Keys="CONTENTS OF .PUB FILE"
However, if I use the below, everything works, but every provided jwt will work, regardless of where it was generated.
labels:
- traefik.http.middlewares.my-traefik-jwt-plugin.plugin.traefik-jwt-plugin="ANY VALUE AT ALL"
Any ideas?
Thanks
The plugin was not imported into Traefik Pilot.
Cause:
failed to run the plugin with Yaegi: the load of the plugin takes too much time, or an error, inside the plugin, occurs during the load: 1:21: import "github.com/team-carepay/traefik-jwt-plugin" error: /tmp/pilot-gop390099581/src/github.com/team-carepay/traefik-jwt-plugin/jwt.go:666:46: cannot use type [32]uint8 as type []uint8
Traefik Plugin Analyzer will restart when you will close this issue.
If you believe there is a problem with the Analyzer or this issue is the result of a false positive, please contact us.
Currently its seems like, you can only access the keys in the jwt token, but not when you want to access the nested maps.
Add a new option to the plugin to pass headers to OPA requests, so for example, authentication to OPA server with Bearer can be configured via plugin params.
As an inspiration, the following PR could be used #56
I'm trying to follow https://firebase.google.com/docs/auth/admin/verify-id-tokens#verify_id_tokens_using_a_third-party_jwt_library so I can validate my firebase JWT tokens with this plugin.
Here is the config :
{
"JwtHeaders":{ "X-Subject": "sub"},
"Keys":[ "https://www.googleapis.com/robot/v1/metadata/x509/[email protected]" ],
"Alg": "RS256",
"PayloadFields":[ "iss", "aud", "auth_time", "user_id", "sub", "iat", "exp", "email", "email_verified", "firebase" ],
"Required": "true"
}
Using it, all my requests are giving: token validation failed
But the token is valid.
However, I'm not sure if I need to provide a private key as shown on : https://jwt.io/ but if I do it raises an error saying that it couldn't extract the key.
Hello @blagerweij
I am proposing to deprecate, i.e. archive https://github.com/team-carepay/traefik-opa-plugin.
That plugin was not updated for long time and is completely superseed by traefik-jwt-plugin
.
The current plugin is much more flexible regarding OPA interaction, allowing to use headers, policy driven status code etc.
Even if users do not want to use JWT verification, they still can use current plugin, e.g. my company uses this plugin without JWT token verification, because we do it on OPA side.
Heya ...
I am trying to extract claims from the token ... but can't seem to get it to work. I am using traefik 2.6.3 and 0.0.6 of this plugin
Config is
experimental:
plugins:
jwt:
moduleName: github.com/team-carepay/traefik-jwt-plugin
version: v0.0.6
and
http:
middlewares:
authorize:
forwardAuth:
address: http://localhost:4180
authResponseHeadersRegex: "(.*)"
extractJwt:
plugin:
jwt:
Required: true
PayloadFields:
- customerId
JwtHeaders:
X-Subject: customerId
When changing the PayloadFields to eg. - fooCustomerId, then traefik complains about the field not being present in the payload (so the actual decoding of the token works)
What doesn't work is getting the X-Subject to be present. I am sending requests through traefik into httpbin, the extracted headers don't make it into the latter. To be precise, I would expect X-Subject to be filled with the customerId claim
When bumping the version to 0.0.9 I get a panic
ERRO[2022-05-03T14:01:12+02:00] Recovered from panic in HTTP handler [127.0.0.1:55115 - /headers]: interface conversion: interface {} is interp.valueInterface, not string middlewareName=traefik-internal-recovery middlewareType=Recovery
ERRO[2022-05-03T14:01:12+02:00] Stack: goroutine 167 [running]:
github.com/traefik/traefik/v2/pkg/middlewares/recovery.recoverFunc({0x111e18220, 0x1400011c058}, 0x14001208600)
github.com/traefik/traefik/v2/pkg/middlewares/recovery/recovery.go:46 +0x204
panic({0x107bf69e0, 0x1400126c550})
runtime/panic.go:838 +0x204
github.com/traefik/yaegi/interp.runCfg.func1()
github.com/traefik/[email protected]/interp/run.go:193 +0x13c
panic({0x107bf69e0, 0x1400126c550})
runtime/panic.go:838 +0x204
github.com/traefik/yaegi/interp.runCfg.func1()
github.com/traefik/[email protected]/interp/run.go:193 +0x13c
panic({0x107bf69e0, 0x1400126c550})
runtime/panic.go:838 +0x204
github.com/traefik/yaegi/interp.runCfg.func1()
github.com/traefik/[email protected]/interp/run.go:193 +0x13c
panic({0x107bf69e0, 0x1400126c550})
runtime/panic.go:838 +0x204
github.com/traefik/yaegi/interp.typeAssert.func3(0x1400122c4d0)
github.com/traefik/[email protected]/interp/run.go:441 +0x534
github.com/traefik/yaegi/interp.runCfg(0x14000dda120, 0x1400122c4d0, 0x14001265588?, 0x1083294c0?)
github.com/traefik/[email protected]/interp/run.go:201 +0x21c
github.com/traefik/yaegi/interp.call.func9(0x1400122c370)
github.com/traefik/[email protected]/interp/run.go:1422 +0x7b4
github.com/traefik/yaegi/interp.runCfg(0x14000dd6360, 0x1400122c370, 0x0?, 0x1083294c0?)
github.com/traefik/[email protected]/interp/run.go:201 +0x21c
github.com/traefik/yaegi/interp.genFunctionWrapper.func2.1({0x14000ee0e40, 0x2, 0x4?})
github.com/traefik/[email protected]/interp/run.go:1023 +0x3a8
github.com/traefik/yaegi/stdlib._net_http_Handler.ServeHTTP(...)
github.com/traefik/[email protected]/stdlib/go1_17_net_http.go:288
github.com/traefik/traefik/v2/pkg/middlewares/auth.(*forwardAuth).ServeHTTP(0x14001187cc0, {0x111e18220, 0x1400011c058}, 0x14001208700)
github.com/traefik/traefik/v2/pkg/middlewares/auth/forward.go:198 +0xbe8
github.com/traefik/traefik/v2/pkg/middlewares/connectionheader.Remover.func1({0x111e18220, 0x1400011c058}, 0x14001208700)
github.com/traefik/traefik/v2/pkg/middlewares/connectionheader/connectionheader.go:34 +0x23c
net/http.HandlerFunc.ServeHTTP(0x140012662a8?, {0x111e18220?, 0x1400011c058?}, 0x0?)
net/http/server.go:2084 +0x3c
reflect.Value.call({0x107e54480?, 0x140011a6bb0?, 0x140012669d8?}, {0x106f85f65, 0x4}, {0x14000ee0a20, 0x2, 0x104d3848c?})
reflect/value.go:556 +0x5e4
reflect.Value.Call({0x107e54480?, 0x140011a6bb0?, 0x14001266a08?}, {0x14000ee0a20, 0x2, 0x2})
reflect/value.go:339 +0x98
github.com/traefik/yaegi/interp.callBin.func1({0x107e54480?, 0x140011a6bb0?, 0x14001266a98?}, {0x14000ee0a20?, 0x140000b7800?, 0x14000f01440?})
github.com/traefik/[email protected]/interp/run.go:1472 +0x2c
github.com/traefik/yaegi/interp.callBin.func10(0x1400122c210)
github.com/traefik/[email protected]/interp/run.go:1646 +0x134
github.com/traefik/yaegi/interp.runCfg(0x1400079ad80, 0x1400122c210, 0x0?, 0x1083294c0?)
github.com/traefik/[email protected]/interp/run.go:201 +0x21c
github.com/traefik/yaegi/interp.genFunctionWrapper.func2.1({0x14000ee0870, 0x2, 0x4?})
github.com/traefik/[email protected]/interp/run.go:1023 +0x3a8
net/http.HandlerFunc.ServeHTTP(0x107bedfa0?, {0x111e18220?, 0x1400011c058?}, 0x14000ee0810?)
net/http/server.go:2084 +0x3c
github.com/traefik/traefik/v2/pkg/middlewares/accesslog.(*FieldHandler).ServeHTTP(0x14000f01500, {0x111e18220, 0x1400011c058}, 0x107d9aec0?)
github.com/traefik/traefik/v2/pkg/middlewares/accesslog/field_middleware.go:29 +0x124
github.com/gorilla/mux.(*Router).ServeHTTP(0x1400119e060, {0x111e18220, 0x1400011c058}, 0x14001208600)
github.com/gorilla/[email protected]/mux.go:141 +0x1e8
github.com/traefik/traefik/v2/pkg/middlewares/recovery.(*recovery).ServeHTTP(0x91a22326606?, {0x111e18220?, 0x1400011c058?}, 0x111e18220?)
github.com/traefik/traefik/v2/pkg/middlewares/recovery/recovery.go:32 +0x70
github.com/traefik/traefik/v2/pkg/middlewares/accesslog.(*FieldHandler).ServeHTTP(0x140011ca940, {0x111e18220, 0x1400011c058}, 0x140011da460?)
github.com/traefik/traefik/v2/pkg/middlewares/accesslog/field_middleware.go:29 +0x124
github.com/traefik/traefik/v2/pkg/middlewares/metrics.(*metricsMiddleware).ServeHTTP(0x140011e67e0, {0x10846a188?, 0x1400011c040}, 0x14001208600)
github.com/traefik/traefik/v2/pkg/middlewares/metrics/metrics.go:121 +0x5f8
github.com/traefik/traefik/v2/pkg/middlewares/snicheck.SNICheck.ServeHTTP({{0x1084452b8?, 0x140011e67e0?}, 0x14000edd020?}, {0x10846a188, 0x1400011c040}, 0x14001208600)
github.com/traefik/traefik/v2/pkg/middlewares/snicheck/snicheck.go:49 +0x114
github.com/traefik/traefik/v2/pkg/middlewares.(*HTTPHandlerSwitcher).ServeHTTP(0x90?, {0x10846a188, 0x1400011c040}, 0x10843d5f0?)
github.com/traefik/traefik/v2/pkg/middlewares/handler_switcher.go:23 +0x64
github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator.(*RequestDecorator).ServeHTTP(0x14000011458, {0x10846a188, 0x1400011c040}, 0x14001208500, 0x14000697bd0)
github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator/request_decorator.go:47 +0x508
github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator.WrapHandler.func1.1({0x10846a188?, 0x1400011c040?}, 0x140007107e0?)
github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator/request_decorator.go:84 +0x68
net/http.HandlerFunc.ServeHTTP(0x1400087eeb0?, {0x10846a188?, 0x1400011c040?}, 0x9?)
net/http/server.go:2084 +0x3c
github.com/traefik/traefik/v2/pkg/middlewares/forwardedheaders.(*XForwarded).ServeHTTP(0x1400087eeb0, {0x10846a188, 0x1400011c040}, 0x14001208500)
github.com/traefik/traefik/v2/pkg/middlewares/forwardedheaders/forwarded_header.go:189 +0xd8
net/http.serverHandler.ServeHTTP({0x140001b2008?}, {0x10846a188, 0x1400011c040}, 0x14001208500)
net/http/server.go:2916 +0x3fc
net/http.initALPNRequest.ServeHTTP({{0x10846b908?, 0x140008bb800?}, 0x1400057e380?, {0x14000134700?}}, {0x10846a188, 0x1400011c040}, 0x14001208500)
net/http/server.go:3523 +0x1f8
net/http.(*http2serverConn).runHandler(0x0?, 0x104cfaf54?, 0x1400057c180?, 0x0?)
net/http/h2_bundle.go:5903 +0x74
created by net/http.(*http2serverConn).processHeaders
net/http/h2_bundle.go:5633 +0x514 middlewareName=traefik-internal-recovery middlewareType=Recovery
Thanks in advance
wirtsi
Hello
Sometimes it's required to return non 403 code but 401 instead, for example to enforce 2FA/MFA cases.
Currently it's impossible because on token / OPA error check the HTTP status code is hardcoded to 403.
Also it could be useful to make success status code OPA driven as well, i.e. extract it from configurable field of OPA response. So, OPA can force redirect to some resource for example.
WDYT ? I am read to attempt to provide a PR for this.
Hello
We would like to use this plugin for OPA based AuthZ implementation, but currently we can not use it for one of the cases.
We'd like to delegate to OPA for decesions for endpoints that are "public", i.e. there no need to pass JWT token for those pages.
But, currently plugin consider Authorization
header as mandatory and immediately fails.
So, I propose to introduce a new parameter that will make Authorization
header optional, so even if header is missing - the decision will still be asked from OPA.
I realize that setting Required
to false is solving my problem, but in this case I will miss required payload claims validation.
What do you think ? In case you're interesting - I'm ready to provide a PR.
The plugin was not imported into Traefik Pilot.
Cause:
failed to run the plugin with Yaegi: the load of the plugin takes too much time(10s), or an error, inside the plugin, occurs during the load: 1:21: import "github.com/team-carepay/traefik-jwt-plugin" error: /tmp/pilot-gop61762835/src/github.com/team-carepay/traefik-jwt-plugin/jwt.go:666:46: cannot use type [32]uint8 as type []uint8
Traefik Plugin Analyzer will restart when you will close this issue.
If you believe there is a problem with the Analyzer or this issue is the result of a false positive, please contact us.
Hello,
We're running into an issue where the plugin will start fetching jwkEndpoints a lot, as opposed to every 15 minutes as I'd expect based on the source code in jwt.go.
I initially discovered this through tracing, and confirmed it by enabling DEBUG level logs in Traefik.
This is how we are configuring the Middleware (headers and namespace cleaned for simplicity):
spec:
plugin:
jwt:
JwtHeaders:
x-head-aud: aud
x-head-exp: exp
x-head-sub: sub
Keys:
- http://http.<namespace>/oidc/v1/jwks
PayloadFields:
- sub
- exp
- aud
Required: true
Checking the Debug log, I noticed "time" is zeroed, and ip, port, url and sub are empty. I wonder if the plugin doesn't really support internal services as key provicers?
Note that everything else seems to work fine with the Middleware, but the constant fetching of the jwks seems to get worse the longer we let the traefik pod run. Once we restart the pod, it seems to go back down to every 15 minutes for a little while before it starts querying jwks non-stop.
The plugin was not imported into Traefik Pilot.
Cause:
failed to get the latest tag: failed to get versions: GET https://api.github.com/repos/team-carepay/traefik-jwt-plugin/tags: 502 []
Traefik Plugin Analyzer will restart when you will close this issue.
If you believe there is a problem with the Analyzer or this issue is the result of a false positive, please contact us.
Seems like custom plugins
are only supported as part of the Traefik enterprise
solution. Is that the case? As stated here in pricing section.
If custom plugins are only supported with Traefik enterprise
, is there any way we can use these custom plugins with Traefik proxy open source
solution or any other work around?
First, great job on this plugin, it's super useful!
We have the scenario where Traefik routes API calls to backend services that are only meant to be used by specific audiences. I.e. we want to ensure that the aud
claim is validated as early as possible; before the request even reaches the target service.
What would be great is if the traefik-jwt-plugin
middleware could be configured to also specify the audience to validate. That way we could assign different jwt-plugin middlewares to different Traefik routes, ensuring that only requests are forwarded where the JWT is not only valid, but was also issued for the correct audience.
Is this something worth considering for this plugin?
The response from Opa is set in the response to the client with the response isn't allowed. While this is useful when developing and debugging in a production system this can lead to a leak of policy and data details to a bad actor.
An example of such responses is {"result":{"allow":false,"cert":[],"customer_id":"<redacted>"}}
Hi,
I'm trying to use this plugin with a simple symmetric key. For example: Eekee9saaBus7shoh1hau6lahYo9ah
.
I tried to specify it in the config like this:
jwt:
Keys:
- Eekee9saaBus7shoh1hau6lahYo9ah
Alg: HS254
But I get the following error:
Invalid configuration, expecting a certificate, public key or JWK URL
The readme stats:
Allowed values include certificates, public keys, symmetric keys.
However, I did not get how to specify my symmetric key here.
Could you give me a hint how to achieve this?
Thank you for your time in advance!
The plugin was not imported into Traefik Plugin Catalog.
Cause:
unsupported plugin: the module name (github.com/team-carepay/traefik-jwt-plugin) doesn't contain the GitHub repository name (github.com/traefik-plugins/traefik-jwt-plugin)
Traefik Plugin Analyzer will restart when you will close this issue.
If you believe there is a problem with the Analyzer or this issue is the result of a false positive, please contact us.
In the readme (https://github.com/team-carepay/traefik-jwt-plugin/blob/main/README.md?plain=1#L54-L55) it states that one can provide values via config for Iss
and Aud
and that these would be "Used to verify" them in the token. Adding those in the config does not seem to have any effect and looking at the source code it seems they are not used in anywhere.
Am I missing something or is the documentation wrong?
edit: removed Alg
as that seems to be actually verified
Hello @blagerweij
At my company we're actively using this plugin and I had already contributed to it as well as I have one more pending PR.
Since this plugin is a base for our core AuthZ functionality we're very interested in being able to contribute more and have faster feedback without creating and maintaining our own fork.
So I'd like to propose mysefl to become a project collaborator, to be able to help you maintain, improve and release new versions faster.
Thanks in advance.
2.10.4
0.5.1
If a request body is JSON array, Traefik always replies with 403 and an error in response body:
json: cannot unmarshal array into Go value of type map[string]interface {}
Seems that the plugin expects a request body to always be a JSON object and JSON array causes an exception to be thrown which results in replying with 403 as a default.
Would be nice to add a support for JSON arrays.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.