Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ experimental:
plugins:
jwt:
moduleName: github.com/agilezebra/jwt-middleware
version: v1.2.9
version: v1.2.10
```
1b. or with command-line options:

```yaml
command:
...
- "--experimental.plugins.jwt.modulename=github.com/agilezebra/jwt-middleware"
- "--experimental.plugins.jwt.version=v1.2.9"
- "--experimental.plugins.jwt.version=v1.2.10"
```

2) Configure and activate the plugin as a middleware in your dynamic traefik config:
Expand Down Expand Up @@ -72,7 +72,7 @@ Name | Description
`forwardToken` | Boolean indicating whether the token should be forwarded to the backend. Default true. If multiple tokens are present in different locations (e.g. cookie and header) and forwarding is false, only the token used will be removed.
`optional` | Validate tokens according to the normal rules but don't require that a token be present. If specific claim requirements are specified in `require` but with `optional` set to `true` and a token is not present, access will be permitted even though the requirements are obviously not met, which may not be what you want or expect. In this case, no headers will be set from claims (as there aren't any). This is quite a niche case but is intended for use on endpoints that support both authorized and anonymous access and you want JWTs verified if present.
`insecureSkipVerify` | A list of issuers' domains for which TLS certificates should not be verified (i.e. use `InsecureSkipVerify: true`). Only the hostname/domain should be specified (i.e. no scheme or trailing slash). Applies to both the openid-configuration and jwks calls.
`rootCAs` | One or more additional root certificate authorities, in PEM format, to be combined with the system cert pool when verifying server certificates.
`rootCAs` | One or more additional root certificate authorities, each expressed either inline in PEM format, or as a path to a file, to be combined with the system cert pool when verifying server certificates.
`infoToStdout` | traefik does not yet have support for plugins to use the logger so, by default, all messages are logged using `log.Printf`, which will send messages from the plugin out as if they were logged at `ERROR` level. This may be irritating for those that don't like to see non-error messages show up as if they are errors. There is a workaround available in that the plugin can send messages to STDOUT and traefik will log these as if they were logged at `DEBUG` level. Setting `infoToStdout` to `true` will send all non-error info messages to STDOUT and these will appear in logs at `DEBUG` level. These will obviously only appear if you set your traefik log level to `DEBUG` (which may actually be more irritating if you don't want the spew that this creates, so this option is not enabled by default). Note also that this workaround does not appear to be working correctly in traefik v2 and in this case you may not see info messages at all if you enable this.

### Template Interpolation
Expand Down
20 changes: 20 additions & 0 deletions jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@ func New(_ context.Context, next http.Handler, config *Config, name string) (htt
return nil, err
}

for index, pem := range config.RootCAs {
pem, err := pemContent(pem)
if err != nil {
return nil, fmt.Errorf("failed to load root CA: %v", err)
}
config.RootCAs[index] = pem
}

plugin := JWTPlugin{
next: next,
name: name,
Expand Down Expand Up @@ -620,6 +628,18 @@ func canonicalizeDomains(domains []string) []string {
return domains
}

// pemContent returns the value if it is alread a PEM or reads the file if it is a filename.
func pemContent(value string) (string, error) {
if value == "" || strings.HasPrefix(value, "-----BEGIN") {
return value, nil
}
content, err := os.ReadFile(value)
if err != nil {
return "", err
}
return string(content), nil
}

// createDefaultClient returns an http.Client with the given root CAs, or a default client if no root CAs are provided.
func createDefaultClient(pems []string, useSystemCertPool bool) *http.Client {
if pems == nil {
Expand Down
24 changes: 24 additions & 0 deletions jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,30 @@ func TestServeHTTP(tester *testing.T) {
Method: jwt.SigningMethodES256,
HeaderName: "Authorization",
},
{
Name: "RootCAs from file",
Expect: http.StatusOK,
Config: `
rootCAs:
- testing/rootca.pem
require:
aud: test`,
Claims: `{"aud": "test"}`,
Method: jwt.SigningMethodES256,
HeaderName: "Authorization",
},
{
Name: "RootCAs from bad file",
ExpectPluginError: "failed to load root CA: open notexist/rootca.pem: no such file or directory",
Config: `
rootCAs:
- notexist/rootca.pem
require:
aud: test`,
Claims: `{"aud": "test"}`,
Method: jwt.SigningMethodES256,
HeaderName: "Authorization",
},
{
Name: "Bad RootCAs",
Expect: http.StatusOK,
Expand Down
4 changes: 4 additions & 0 deletions testing/ec-public.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN EC PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEE7gFCo/g2PQmC3i5kIqVgCCzr2D1
nbCeipqfvK1rkqmKfhb7rlVehfC7ITUAy8NIvQ/AsXClvgHDv55BfOoL6w==
-----END EC PUBLIC KEY-----
19 changes: 19 additions & 0 deletions testing/rootca.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDJzCCAg+gAwIBAgIUDDYN8pGCpUC6tsqDW4meIXsmN04wDQYJKoZIhvcNAQEL
BQAwIzELMAkGA1UEBhMCVUsxFDASBgNVBAoMC0FnaWxlIFplYnJhMB4XDTI1MDMx
MTE0MTU1MloXDTM1MDMwOTE0MTU1MlowIzELMAkGA1UEBhMCVUsxFDASBgNVBAoM
C0FnaWxlIFplYnJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA70Gs
A3QEKB94Eqyt+V07qDNtykhlyOLSiGIRk1/Slr5B1mTY8Mt88gg8MFldyVukjze+
/5GT/lZ3plMMiA7wnpJ683iWqMVOzQTtYlgcMknnrRJhHuDIGmcdakudXl484emE
9iz+cWgl2cw1rb0rtNC1koQ90MohcTqW+5By0TUaulf80ZcJbGFG8LTqVKVJatET
QedgrYR3tIR6VRtj7pnFZ1w9gZhpPL26mrMg3Wk3GHf/j48jebHVYbeuuSoBXJX8
rGmfCtwzMWqyZvMU9MRP6KpPu20UIOuzau6JyD22RhlLSrX/1eI9Et0IMqEF/iM/
EGpTGDJTeX3bJavzAQIDAQABo1MwUTAdBgNVHQ4EFgQUwR3igK8QvKXQ3JuGlYUc
1jHwBqUwHwYDVR0jBBgwFoAUwR3igK8QvKXQ3JuGlYUc1jHwBqUwDwYDVR0TAQH/
BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAoEgu6gQTf8Br0Id7Jp6Oht6XSG0o
RtYJ4SwWD0U1acJpWKgtTkBA9cfGMYngFzUe9Xmxt1iBSCJtbQ/SQj5x0vcXsoR0
zWBnihf3XERnJOyLWR7cUCfVYEu0xFCNrc1m5Wzj4IG2NJBTtiIiAdnTbEcBd7hk
f7Vy+al187qn3HQcwdRfMatjFrrM92tHvd79VJsZcgj8Yl3QcgZFIQ2O+PtrXxLR
2auMwVTxdRe0QUT6zvtZGf1niNH5s8DBVeDWqBArlC7M/HuLj6QOIMDEI2aC3yS1
LT12fZ0MWBjfGc90EEJ9z4/CRUWMdtlOaLnXinyrvOH+SSTJD8xfwKqH6g==
-----END CERTIFICATE-----
Loading