-
Notifications
You must be signed in to change notification settings - Fork 273
Add jwt auth filter config proto #530
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
Changes from 26 commits
7a3129f
30ee783
cfefc28
529471d
71a5490
89720dd
062cdf8
f20a77d
95cfa93
4bbf204
335ec91
2f1aa17
648cd3e
42fc1f7
d5b1617
888073f
ef51a27
fea7712
dba79ad
955ddbf
cec86ef
64d6694
7625319
4ca5e41
937d521
b21a927
3c8bf24
7dfc0f9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| licenses(["notice"]) # Apache 2 | ||
|
|
||
| load("//bazel:api_build_system.bzl", "api_proto_library") | ||
|
|
||
| api_proto_library( | ||
| name = "jwt_authn", | ||
| srcs = ["config.proto"], | ||
| deps = [ | ||
| "//envoy/api/v2/core:base", | ||
| "//envoy/api/v2/core:http_uri", | ||
| "//envoy/api/v2/route", | ||
| ], | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # JWT Authentication HTTP filter config | ||
|
|
||
| ## Overview | ||
|
|
||
| 1. The proto file in this folder defines an HTTP filter config for "jwt_authn" filter. | ||
|
|
||
| 2. This filter will verify the JWT in the HTTP request as: | ||
| - The signature should be valid | ||
| - JWT should not be expired | ||
| - Issuer and audiences are valid and specified in the filter config. | ||
|
|
||
| 3. [JWK](https://tools.ietf.org/html/rfc7517#appendix-A) is needed to verify JWT signature. It can be fetched from a remote server or read from a local file. If the JWKS is fetched remotely, it will be cached by the filter. | ||
|
|
||
| 3. If a JWT is valid, the user is authenticated and the request will be forwarded to the backend server. If a JWT is not valid, the request will be rejected with an error message. | ||
|
|
||
| ## The locations to extract JWT | ||
|
|
||
| JWT will be extracted from the HTTP headers or query parameters. The default location is the HTTP header: | ||
| ``` | ||
| Authorization: Bearer <token> | ||
| ``` | ||
| The next default location is in the query parameter as: | ||
| ``` | ||
| ?access_token=<TOKEN> | ||
| ``` | ||
|
|
||
| If a custom location is desired, `from_headers` or `from_params` can be used to specify custom locations to extract JWT. | ||
|
|
||
| ## HTTP header to pass sucessfully verified JWT | ||
|
|
||
| If a JWT is valid, its payload will be passed to the backend in a new HTTP header specified in `forward_payload_header` field. Its value is base64 encoded JWT payload in JSON. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,223 @@ | ||
|
|
||
| syntax = "proto3"; | ||
|
|
||
| package envoy.config.filter.http.jwt_authn.v2; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No "_" in package name please.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @wora this is necessary if the path has underscore for consistency and some Go stuff.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are the settlement of #458? Should this be v2alpha?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Waiting for the decision, before that leave it "v2" for now.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we have consensus there, let's make this
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
|
||
| import "envoy/api/v2/core/base.proto"; | ||
| import "envoy/api/v2/core/http_uri.proto"; | ||
| import "envoy/api/v2/route/route.proto"; | ||
| import "google/protobuf/duration.proto"; | ||
| import "validate/validate.proto"; | ||
|
|
||
| // This message specifies how a JSON Web Token (JWT) can be verified. JWT format is defined | ||
| // `here <https://tools.ietf.org/html/rfc7519>`_. Please see `OAuth2.0 | ||
| // <https://tools.ietf.org/html/rfc6749>`_ and `OIDC1.0 <http://openid.net/connect>`_ for | ||
| // the authentication flow. | ||
| // | ||
| // Example: | ||
| // | ||
| // .. code-block:: yaml | ||
| // | ||
| // issuer: https://example.com | ||
| // audiences: | ||
| // - bookstore_android.apps.googleusercontent.com | ||
| // bookstore_web.apps.googleusercontent.com | ||
| // remote_jwks: | ||
| // - http_uri: | ||
| // - uri: https://example.com/.well-known/jwks.json | ||
| // cluster: example_jwks_cluster | ||
| // cache_duration: | ||
| // - seconds: 300 | ||
| // | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: might be worth adding
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
| // [#not-implemented-hide:] | ||
| message JwtRule { | ||
| // Identifies the principal that issued the JWT. See `here | ||
| // <https://tools.ietf.org/html/rfc7519#section-4.1.1>`_. Usually a URL or an email address. | ||
| // | ||
| // Example: https://securetoken.google.com | ||
| // Example: 1234567-compute@developer.gserviceaccount.com | ||
| // | ||
| string issuer = 1 [(validate.rules).string.min_bytes = 1]; | ||
|
|
||
| // The list of JWT `audiences <https://tools.ietf.org/html/rfc7519#section-4.1.3>`_. that are | ||
| // allowed to access. A JWT containing any of these audiences will be accepted. If not specified, | ||
| // will not check audiences in the token. | ||
| // | ||
| // Example: | ||
| // | ||
| // .. code-block:: yaml | ||
| // | ||
| // audiences: | ||
| // - bookstore_android.apps.googleusercontent.com | ||
| // bookstore_web.apps.googleusercontent.com | ||
| // | ||
| repeated string audiences = 2; | ||
|
|
||
| // `JSON Web Key Set <https://tools.ietf.org/html/rfc7517#appendix-A>`_ is needed. to validate | ||
| // signature of the JWT. This field specifies where to fetch JWKS. | ||
| oneof jwks_source_specifier { | ||
| option (validate.required) = true; | ||
|
|
||
| // JWKS can be fetched from remote server via HTTP/HTTPS. This field specifies the remote HTTP | ||
| // URI and how the fetched JWKS should be cached. | ||
| // | ||
| // Example: | ||
| // | ||
| // .. code-block:: yaml | ||
| // | ||
| // remote_jwks: | ||
| // - http_uri: | ||
| // - uri: https://www.googleapis.com/oauth2/v1/certs | ||
| // cluster: jwt.www.googleapis.com|443 | ||
| // cache_duration: | ||
| // - seconds: 300 | ||
| // | ||
| RemoteJwks remote_jwks = 3; | ||
|
|
||
| // JWKS is in local data source. It could be either in a local file or embedded in the | ||
| // inline_string. | ||
| // | ||
| // Example: local file | ||
| // | ||
| // .. code-block:: yaml | ||
| // | ||
| // local_jwks: | ||
| // - filename: /etc/envoy/jwks/jwks1.txt | ||
| // | ||
| // Example: inline_string | ||
| // | ||
| // .. code-block:: yaml | ||
| // | ||
| // local_jwks: | ||
| // - inline_string: "ACADADADADA" | ||
| // | ||
| envoy.api.v2.core.DataSource local_jwks = 4; | ||
| } | ||
|
|
||
| // If false, the JWT is removed in the request after a success verification. If true, the JWT is | ||
| // not removed in the request. Default value is false. | ||
| bool forward = 5; | ||
|
|
||
| // Two fields below define where to extract the JWT from an HTTP request. | ||
| // | ||
| // If no explicit location is specified, the following default locations are tried in order: | ||
| // | ||
| // 1. The Authorization header using the Bearer schema. See `here | ||
| // <https://tools.ietf.org/html/rfc6750#section-2.1>`_. Example: | ||
| // | ||
| // Authorization: Bearer <token>. | ||
| // | ||
| // 2. `access_token` query parameter. See `this | ||
| // <https://tools.ietf.org/html/rfc6750#section-2.3>`_ | ||
| // | ||
|
|
||
| // Multiple JWTs can be verified for a request. Each JWT has to be extracted from the locations | ||
| // its issuer specified or from the default locations. | ||
|
|
||
| // Specify the HTTP headers to extract JWT token. For examples, following config: | ||
| // | ||
| // .. code-block:: yaml | ||
| // | ||
| // from_headers: | ||
| // - name: x-goog-iap-jwt-assertion | ||
| // | ||
| // can be used to extract token from header:: | ||
| // | ||
| // x-goog-iap-jwt-assertion: <JWT>. | ||
| // | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens when more than one header matches? Does the first match win? Please udpate the comment with this, same with
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Multiple JWTs will be supported. Multiple headers can carry multiple tokens, they all will be verified. Add comment for this |
||
| repeated JwtHeader from_headers = 6; | ||
|
|
||
| // JWT is sent in a query parameter. `jwt_params` represents the query parameter names. | ||
| // | ||
| // For example, if config is: | ||
| // | ||
| // .. code-block:: yaml | ||
| // | ||
| // from_params: | ||
| // - jwt_token | ||
| // | ||
| // The JWT format in query parameter is:: | ||
| // | ||
| // /path?jwt_token=<JWT> | ||
| // | ||
| repeated string from_params = 7; | ||
|
|
||
| // This field specifies the header name to forward a successfully verified JWT payload to the | ||
| // backend. The forwarded data is:: | ||
| // | ||
| // base64_encoded(jwt_payload_in_JSON) | ||
| // | ||
| // If it is not specified, the payload will not be forwarded. If multiple JWT payloads needed to | ||
| // be forwarded, distinct header names are required. | ||
| string forward_payload_header = 8; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still waiting for this one to be made clearer, I still can't get clear in my head what the above sentence means given this is a singleton. If it's confusing me (and admittedly, I'm not an expert in this area), I think it will confuse others, so can you try and re-express this to reduce the chance of this?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about this? |
||
| } | ||
|
|
||
| // This message specifies how to fetch JWKS from remote and how to cache it. | ||
| message RemoteJwks { | ||
| // The HTTP URI to fetch the JWKS. For example: | ||
| // | ||
| // .. code-block:: yaml | ||
| // | ||
| // http_uri: | ||
| // - uri: https://www.googleapis.com/oauth2/v1/certs | ||
| // cluster: jwt.www.googleapis.com|443 | ||
| // | ||
| envoy.api.v2.core.HttpUri http_uri = 1; | ||
|
|
||
| // Duration after which the cached JWKS should be expired. If not specified, default cache | ||
| // duration is 5 minutes. | ||
| google.protobuf.Duration cache_duration = 2; | ||
| } | ||
|
|
||
| // This message specifies a header location to extract JWT token. | ||
| message JwtHeader { | ||
| // The HTTP header name. | ||
| string name = 1 [(validate.rules).string.min_bytes = 1]; | ||
|
|
||
| // The value prefix. The value format is "value_prefix<token>" | ||
| // For example, for "Authorization: Bearer <token>", value_prefix="Bearer " with a space at the | ||
| // end. | ||
| string value_prefix = 2; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this always what you want, or would you prefer regex? No strong opinion, just curious.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Usually we want the whole value. Only "Authorization: Bearer " has a "Bearer " prefix. regex is over-kill. |
||
| } | ||
|
|
||
| // This is the Envoy HTTP filter config for JWT authentication. | ||
| // [#not-implemented-hide:] | ||
| message JwtAuthentication { | ||
| // List of JWT rules to valide. | ||
| repeated JwtRule rules = 1; | ||
|
|
||
| // If true, the request is allowed if JWT is missing or JWT verification fails. | ||
| // Default is false, a request without JWT or failed JWT verification is not allowed. | ||
| bool allow_missing_or_failed = 2; | ||
|
|
||
| // This field lists the patterns allowed to bypass JWT verification. This only applies when | ||
| // `allow_missing_or_failed_jwt` is false. Under this config, if a request doesn't have JWT, it | ||
| // will be rejected. But some requests still needed to be forwarded without JWT, such as OPTIONS | ||
| // for CORS and some health checking paths. | ||
| // | ||
| // Examples: bypass all CORS options requests | ||
| // | ||
| // .. code-block:: yaml | ||
| // | ||
| // bypass: | ||
| // - headers: | ||
| // - name: :method | ||
| // value: OPTIONS | ||
| // - headers: | ||
| // - name: :path | ||
| // regex_match: /.* | ||
| // | ||
| // Examples: bypass /healthz check | ||
| // | ||
| // .. code-block:: yaml | ||
| // | ||
| // bypass: | ||
| // - headers: | ||
| // - name: :method | ||
| // value: GET | ||
| // - headers: | ||
| // - name: :path | ||
| // exact_match: /healthz | ||
| // | ||
| repeated envoy.api.v2.route.RouteMatch bypass = 3; | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might need an action here (or in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
jwt_authz .. not authn
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it should be authn. "authz" is for authorization. This proxy is only for token verification, it is authentication, it is "authn".