Hello Envoy team
I'm trying to implement oauth, using envoy 1.16.0 I read the documentation provided at the following link https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/oauth2_filter, I dont think the documentation is correct, since I tried to use the sample describe and its not working
this is the error I'm facing, can you please update the documentation and provide a good example?
Unable to parse JSON as proto (INVALID_ARGUMENT:(http_filters[0].typed_config): invalid value Invalid type URL, unknown type: envoy.extensions.filters.http.oauth2.v3alpha.OAuth2 for type Any): {"clusters":[{"lb_policy":"ROUND_ROBIN","load_assignment":{"endpoints":[{"lb_endpoints":[{"endpoint":{"address":{"socket_address":{"address":"auth.example.com","port_value":443}}}}]}],"cluster_name":"auth"},"name":"auth","tls_context":{"sni":"auth.example.com"},"type":"LOGICAL_DNS","connect_timeout":"5s"}],"static_resources":{"listeners":[{"address":{"socket_address":{"address":"0.0.0.0","port_value":443}},"name":"oauth","filter_chains":[{"filters":[{"name":"envoy.filters.network.http_connection_manager","typed_config":{"http_filters":[{"typed_config":{"config":{"redirect_uri":"%REQ(:x-forwarded-proto)%://%REQ(:authority)%/callback","signout_path":{"path":{"exact":"/signout"}},"credentials":{"client_id":"foo"},"token_endpoint":{"uri":"oauth.com/token","cluster":"oauth","timeout":"3s"},"redirect_path_matcher":{"path":{"exact":"/callback"}},"authorization_endpoint":"https://oauth.com/oauth/authorize/"},"@type":"type.googleapis.com/envoy.extensions.filters.http.oauth2.v3alpha.OAuth2"},"name":"envoy.filters.http.oauth2"},{"name":"envoy.router"}],"@type":"type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"}}]}]}]}}
this is my configuration file
static_resources:
listeners:
- name: oauth
address:
socket_address:
address: 0.0.0.0
port_value: 443
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
http_filters:
- name: envoy.filters.http.oauth2
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3alpha.OAuth2
config:
token_endpoint:
cluster: oauth
uri: oauth.com/token
timeout: 3s
authorization_endpoint: https://oauth.com/oauth/authorize/
redirect_uri: "%REQ(:x-forwarded-proto)%://%REQ(:authority)%/callback"
redirect_path_matcher:
path:
exact: /callback
signout_path:
path:
exact: /signout
credentials:
client_id: foo
token_secret:
name: token
sds_config:
path: "/etc/envoy/token-secret.yaml"
- name: envoy.router
clusters:
- name: auth
connect_timeout: 5s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: auth
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: auth.example.com
port_value: 443
tls_context:
sni: auth.example.com
also is there any special format for the token file? or how can we pass the token secret?
The error indicates that the OAuth2 filter isn't compiled into the binary. Are you building a custom set of extensions, implicitly disabling the OAuth filter?
The error indicates that the OAuth2 filter isn't compiled into the binary. Are you building a custom set of extensions, implicitly disabling the OAuth filter?
I'm using the latest docker version, does this version doesnt come with Oauth?
It does. I get a different error than you when I run your config against envoyproxy/envoy-alpine:v1.16.0, can you verify that the binary you're using is actually 1.16?
It does. I get a different error than you when I run your config against
envoyproxy/envoy-alpine:v1.16.0, can you verify that the binary you're using is actually 1.16?
now I got this error, not sure what it means, is this the error you are getting too?
[2020-10-20 15:43:17.357][8][info][main] [source/server/server.cc:731] exiting
Proto constraint validation failed (HttpConnectionManagerValidationError.StatPrefix: ["value length must be at least " '\x01' " runes"]): http_filters {
name: "envoy.filters.http.oauth2"
typed_config {
[type.googleapis.com/envoy.extensions.filters.http.oauth2.v3alpha.OAuth2] {
config {
token_endpoint {
uri: "oauth.com/token"
cluster: "oauth"
timeout {
seconds: 3
}
}
authorization_endpoint: "https://oauth.com/oauth/authorize/"
credentials {
client_id: "foo"
token_secret {
name: "token"
sds_config {
path: "/etc/envoy/token-secret.yaml"
}
}
hmac_secret {
name: "hmac"
sds_config {
path: "/etc/envoy/hmac.yaml"
}
}
}
redirect_uri: "%REQ(:x-forwarded-proto)%://%REQ(:authority)%/callback"
redirect_path_matcher {
path {
exact: "/callback"
}
}
signout_path {
path {
exact: "/signout"
}
}
}
}
}
}
http_filters {
name: "envoy.router"
}
Ok yeah it seems like the example is bad, it's missing the route config for the HCM config and the stat prefix.
cc @dio in case you have time to come up with a fix
also an example of the content of token secret file and hmac
Thank you
cc @rgs1 as well
Another question @rgs1 ,@dio, @snowp I'm planning to have envoy running in a server that will route my request to another app www.myapp.com, this app doesnt have any authorization defined, planning to use envoy to do the authorization and send it to my app
based on this, what should be in the redirect_uri key? myapp.com or my envoy instance /callback and with the call back a route config to my app.com?
also, how can I set the oidc token in a cookie and redirect it to myapp.com?
@juanvasquezreyes
Your redirect_uri key should be the same uri used in the initial request before the redirect to the authn server occurs. The provided example %REQ(:x-forwarded-proto)%://%REQ(:authority)%/callback is a great start, assuming you're okay with the /callback path being the filter's path to match on to determine whether or not it is handling a redirected request.
When the authn server validates the client and returns an authorization token back to the OAuth filter, no matter what format that token is, if forward_bearer_token is set to true the filter will send over a cookie named BearerToken to the upstream. Hopefully this helps.
@snowp could you figure out what is missing in the config below. I'm running into a similar issue here
error: Protobuf message (type envoy.config.bootstrap.v3.Bootstrap reason INVALID_ARGUMENT:clusters: Cannot find field.) has unknown fields
static_resources:
listeners:
- name: main
address:
socket_address:
address: 0.0.0.0
port_value: 8081
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
http_filters:
- name: envoy.filters.http.oauth2
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3alpha.OAuth2
config:
token_endpoint:
cluster: oauth
uri: oauth.com/token
timeout: 3s
authorization_endpoint: https://okta.com/oauth2/id/v1/authorize
redirect_uri: "%REQ(:x-forwarded-proto)%://%REQ(:authority)%/oauth2/callback"
redirect_path_matcher:
path:
exact: /oauth2/callback
signout_path:
path:
exact: /signout
credentials:
client_id: id_hash
token_secret:
name: token
sds_config:
path: "/etc/envoy/token-secret.yaml"
hmac_secret:
name: hmac
sds_config:
path: "/etc/envoy/hmac.yaml"
- name: envoy.filters.http.router
clusters:
- name: oauth
connect_timeout: 3s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: oauth
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: test.okta.com
port_value: 443
tls_context:
sni: test.okta.com
Version: 1.17
thank you!
@snowp could you figure out what is missing in the config below. I'm running into a similar issue here
error:
Protobuf message (type envoy.config.bootstrap.v3.Bootstrap reason INVALID_ARGUMENT:clusters: Cannot find field.) has unknown fieldsstatic_resources: listeners: - name: main address: socket_address: address: 0.0.0.0 port_value: 8081 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager http_filters: - name: envoy.filters.http.oauth2 typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3alpha.OAuth2 config: token_endpoint: cluster: oauth uri: oauth.com/token timeout: 3s authorization_endpoint: https://okta.com/oauth2/id/v1/authorize redirect_uri: "%REQ(:x-forwarded-proto)%://%REQ(:authority)%/oauth2/callback" redirect_path_matcher: path: exact: /oauth2/callback signout_path: path: exact: /signout credentials: client_id: id_hash token_secret: name: token sds_config: path: "/etc/envoy/token-secret.yaml" hmac_secret: name: hmac sds_config: path: "/etc/envoy/hmac.yaml" - name: envoy.filters.http.router clusters: - name: oauth connect_timeout: 3s type: LOGICAL_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: oauth endpoints: - lb_endpoints: - endpoint: address: socket_address: address: test.okta.com port_value: 443 tls_context: sni: test.okta.comVersion: 1.17
thank you!
Not sure if that si the way that is supposed to work, but I was able to fix those issues
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
tracing: {}
codec_type: "AUTO"
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains:
- '*'
routes:
- match:
prefix: /callback
route: mycluster
what I still dont understand is the /callback, I'm getting an issue "Oauth flow failed.",
Also not sure why hmac is required, I'm trying to implement it like this link https://toolsqa.com/wp-content/uploads/sites/1/nggallery/postman/Get_Access_Token_Values.png
Maybe a full example of this implementation will be appreciated
@juanvasquezreyes
I will try to improve the documentation for this filter this weekend.
The conventional OAuth flow involves:
myapp.com, and the oauth filter redirects them to the authorization_endpoint for login. The client ID is sent in the query string in this first redirect.myapp.com/callback, assuming you have chosen /callback as your match path inside the Envoy config. An "authorization grant" is included in the query string for this second redirect.token_secret, the filter then attempts to retrieve an access token from the token_endpoint. The filter knows to do this instead of reinitiating another login because the incoming request has a path that matches the redirect_path_matcher criteria.So if you're getting "OAuth flow failed" at /callback, I assume your token endpoint is denying your configured secret or the grant obtained in step 2.
You may also notice that hmac_secret is never mentioned in that flow - the HMAC is important because the filter, upon a full completion of the OAuth flow, can set cookies as a method of convenience to the user and bypass future login requirements until a certain expiry. To prevent the risk of a request injecting these cookies manually and skipping OAuth freely, we require another secret to help derive an HMAC from the access token, host header, etc., ensuring cookie integrity.
@juanvasquezreyes thanks for your reply. Do you mind posting your current full confg so I can take a look into it?
Thanks
@juanvasquezreyes thanks for your reply. Do you mind posting your current full confg so I can take a look into it?
Thanks
sure, but is not working as I want
admin:
access_log_path: /tmp/admin_access.log
address:
socket_address:
address: 0.0.0.0
port_value: 9901
static_resources:
listeners:
- name: oauth
address:
socket_address:
address: 0.0.0.0
port_value: 443
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
tracing: {}
codec_type: "AUTO"
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains:
- '*'
routes:
- match:
prefix: /callback
route:
cluster: myapp
http_filters:
- name: envoy.filters.http.oauth2
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3alpha.OAuth2
config:
token_endpoint:
cluster: oauth
uri: <keycloak-server>/token
timeout: 3s
authorization_endpoint: <keycloak-server>/auth
redirect_uri: "<envoyserver>/callback"
redirect_path_matcher:
path:
exact: /callback
signout_path:
path:
exact: /signout
credentials:
client_id: <client id>
token_secret:
name: token
sds_config:
path: "/etc/tokensecret.yaml"
hmac_secret:
name: hmac
sds_config:
path: "/etc/hmacsecret.yaml"
- name: envoy.router
tls_context:
common_tls_context:
tls_certificates:
- certificate_chain:
filename: "cert.pem"
private_key:
filename: "key.pem"
clusters:
- name: oauth
connect_timeout: 5s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: auth
endpoints:
- lb_endpoints:
- endpoint:
address: { socket_address: { address: <keycloak-server>, port_value: 443 }}
tls_context: { sni: <keycloak-server> }
- name: myapp
connect_timeout: 0.25s
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: myapp
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: myapp.com
port_value: 443
in the tokensecret.yaml I have this, and its similar to what I have in hmacsecret.yaml
resources:
- "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret"
name: token
generic_secret:
secret:
inline_string: <tokensecret>
and when I go to https://envoyserver it is redirecting to my keycloak and after authenticating Its redirecting to https://envoyserver/callback and is supposed to be sending to myapp but instead of that I'm getting this error "OAuth flow failed."
@juanvasquezreyes using your config file I'm now able to run envoy, at least. Thank you!.
But it is not working yet.
I'm running the docker image locally, and when I request localhost:8080 I get an empty response from the server (envoy)...this is not even going through the filter yet.
And I don't see errors in the log.
@juanvasquezreyes using your config file I'm now able to run envoy, at least. Thank you!.
But it is not working yet.
I'm running the docker image locally, and when I request localhost:8080 I get an empty response from the server (envoy)...this is not even going through the filter yet.
And I don't see errors in the log.
that is because the listener I'm using is not using port 8080
So I've been working on getting keycloak integration with envoy for a while and have created a repo to get a poc out for getting this to work:
https://github.com/krubot/envoy-keycloak-oauth
However, from the envoy logs when running this I get the following output which seems to be causing the OAuth flow failed response:
proxy | [2020-10-25 15:33:05.628][29][debug][router] [source/common/router/router.cc:429] [C0][S2257522640722456408] cluster 'keycloak' match for URL '/auth/realms/api/protocol/openid-connect/token'
proxy | [2020-10-25 15:33:05.628][29][debug][router] [source/common/router/router.cc:586] [C0][S2257522640722456408] router decoding headers:
proxy | ':path', '/auth/realms/api/protocol/openid-connect/token'
proxy | ':authority', '127.0.0.1:8080'
proxy | ':method', 'POST'
proxy | ':scheme', 'http'
proxy | 'content-type', 'application/x-www-form-urlencoded'
proxy | 'x-envoy-internal', 'true'
proxy | 'x-forwarded-for', '172.24.0.5'
proxy | 'x-envoy-expected-rq-timeout-ms', '5000'
proxy |
proxy | [2020-10-25 15:33:05.628][29][debug][pool] [source/common/http/conn_pool_base.cc:71] queueing stream due to no available connections
proxy | [2020-10-25 15:33:05.628][29][debug][pool] [source/common/conn_pool/conn_pool_base.cc:104] creating a new connection
proxy | [2020-10-25 15:33:05.628][29][debug][client] [source/common/http/codec_client.cc:39] [C1] connecting
proxy | [2020-10-25 15:33:05.628][29][debug][connection] [source/common/network/connection_impl.cc:755] [C1] connecting to 172.24.0.3:8080
proxy | [2020-10-25 15:33:05.628][29][debug][connection] [source/common/network/connection_impl.cc:771] [C1] connection in progress
proxy | [2020-10-25 15:33:05.629][29][debug][connection] [source/common/network/connection_impl.cc:611] [C1] connected
proxy | [2020-10-25 15:33:05.629][29][debug][client] [source/common/http/codec_client.cc:77] [C1] connected
proxy | [2020-10-25 15:33:05.629][29][debug][pool] [source/common/conn_pool/conn_pool_base.cc:205] [C1] attaching to next stream
proxy | [2020-10-25 15:33:05.629][29][debug][pool] [source/common/conn_pool/conn_pool_base.cc:126] [C1] creating stream
proxy | [2020-10-25 15:33:05.629][29][debug][router] [source/common/router/upstream_request.cc:357] [C0][S2257522640722456408] pool ready
proxy | [2020-10-25 15:33:05.647][29][debug][router] [source/common/router/router.cc:1178] [C0][S2257522640722456408] upstream headers complete: end_stream=false
proxy | [2020-10-25 15:33:05.647][29][debug][http] [source/common/http/async_client_impl.cc:100] async http request response headers (end_stream=false):
proxy | ':status', '400'
proxy | 'cache-control', 'no-store'
proxy | 'x-xss-protection', '1; mode=block'
proxy | 'pragma', 'no-cache'
proxy | 'x-frame-options', 'SAMEORIGIN'
proxy | 'referrer-policy', 'no-referrer'
proxy | 'date', 'Sun, 25 Oct 2020 15:33:05 GMT'
proxy | 'connection', 'keep-alive'
proxy | 'strict-transport-security', 'max-age=31536000; includeSubDomains'
proxy | 'x-content-type-options', 'nosniff'
proxy | 'content-type', 'application/json'
proxy | 'content-length', '84'
proxy | 'x-envoy-upstream-service-time', '18'
proxy |
proxy | [2020-10-25 15:33:05.647][29][debug][client] [source/common/http/codec_client.cc:109] [C1] response complete
proxy | [2020-10-25 15:33:05.647][29][debug][upstream] [source/extensions/filters/http/oauth2/oauth_client.cc:67] Oauth response code: 400
proxy | [2020-10-25 15:33:05.647][29][debug][http] [source/common/http/filter_manager.cc:805] [C0][S2272988063471314909] Sending local reply with details
proxy | [2020-10-25 15:33:05.647][29][debug][http] [source/common/http/conn_manager_impl.cc:1435] [C0][S2272988063471314909] encoding headers via codec (end_stream=false):
proxy | ':status', '401'
proxy | 'content-length', '18'
proxy | 'content-type', 'text/plain'
proxy | 'date', 'Sun, 25 Oct 2020 15:33:05 GMT'
proxy | 'server', 'envoy'
proxy |
proxy | [2020-10-25 15:33:05.647][29][debug][pool] [source/common/http/http1/conn_pool.cc:50] [C1] response complete
proxy | [2020-10-25 15:33:05.647][29][debug][pool] [source/common/conn_pool/conn_pool_base.cc:151] [C1] destroying stream: 0 remaining
Not sure why I'm getting back a 400 response here but this looks to be in issue.
@krubot
Looks like the OAuth filter's token_endpoint client was committed with some bugs. I'll send a PR today with the promised documentation.
@juanvasquezreyes using your config file I'm now able to run envoy, at least. Thank you!.
But it is not working yet.
I'm running the docker image locally, and when I request localhost:8080 I get an empty response from the server (envoy)...this is not even going through the filter yet.
And I don't see errors in the log.that is because the listener I'm using is not using port 8080
I changed to use 8080 in my config file, and I have and app listening to this port.
I see this warning in envoy logs: [warning][config] [source/common/config/filesystem_subscription_impl.cc:43] Filesystem config update rejected: Unexpected SDS secret (expecting hmac): token
@krubot
Checked from the master am still getting "Oauth response code: 400". Please update here if it is working for you. Thanks
I used "envoyproxy/envoy-dev" docker image.
@juanvasquezreyes using your config file I'm now able to run envoy, at least. Thank you!.
But it is not working yet.
I'm running the docker image locally, and when I request localhost:8080 I get an empty response from the server (envoy)...this is not even going through the filter yet.
And I don't see errors in the log.that is because the listener I'm using is not using port 8080
I changed to use 8080 in my config file, and I have and app listening to this port.
I see this warning in envoy logs:[warning][config] [source/common/config/filesystem_subscription_impl.cc:43] Filesystem config update rejected: Unexpected SDS secret (expecting hmac): token
Check whether you are using the same name in both tokensecret.yaml and hmac.yaml, If yes change the name .
I just tested it and looks like is working for me this is the full configuration file I'm using, its using port 443, if you need another port just change it
I'm using this image to test envoyproxy/envoy-dev
in your oauth server you have to put as valid redirect url the server where envoy is running
admin:
access_log_path: /tmp/admin_access.log
address:
socket_address:
address: 0.0.0.0
port_value: 9901
static_resources:
secrets:
- name: token
generic_secret:
secret:
inline_string: <token_secret>
- name: hmac
generic_secret:
secret:
inline_string: <hmac>
listeners:
- name: oauth
address:
socket_address:
address: 0.0.0.0
port_value: 443
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
tracing: {}
codec_type: "AUTO"
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains:
- '*'
routes:
- match:
prefix: /
route:
cluster: myapp
http_filters:
- name: envoy.filters.http.oauth2
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.oauth2.v3alpha.OAuth2
config:
token_endpoint:
cluster: oauth
uri: <oauth_server>/openid-connect/token/
timeout: 3s
authorization_endpoint: <oauth_server>/openid-connect/auth
redirect_uri: "https://<envoy_server>/callback"
redirect_path_matcher:
path:
exact: /callback
signout_path:
path:
exact: /signout
credentials:
client_id: <clien_id>
token_secret:
name: token
hmac_secret:
name: hmac
- name: envoy.router
tls_context: # THIS IS FOR HTTPS
common_tls_context:
tls_certificates:
- certificate_chain:
filename: "/etc/server.pem"
private_key:
filename: "/etc/key.pem"
clusters:
- name: myapp
connect_timeout: 0.25s
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: myapp
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: myapp.com
port_value: 443
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext
sni: myapp.com
- name: oauth
connect_timeout: 5s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: oauth
endpoints:
- lb_endpoints:
- endpoint:
address: { socket_address: { address: <oauth_server>, port_value: 443 }}
tls_context: { sni: <oauth_server> }
Most helpful comment
@juanvasquezreyes
I will try to improve the documentation for this filter this weekend.
The conventional OAuth flow involves:
myapp.com, and the oauth filter redirects them to theauthorization_endpointfor login. The client ID is sent in the query string in this first redirect.myapp.com/callback, assuming you have chosen/callbackas your match path inside the Envoy config. An "authorization grant" is included in the query string for this second redirect.token_secret, the filter then attempts to retrieve an access token from thetoken_endpoint. The filter knows to do this instead of reinitiating another login because the incoming request has a path that matches theredirect_path_matchercriteria.So if you're getting "OAuth flow failed" at
/callback, I assume your token endpoint is denying your configured secret or the grant obtained in step 2.You may also notice that
hmac_secretis never mentioned in that flow - the HMAC is important because the filter, upon a full completion of the OAuth flow, can set cookies as a method of convenience to the user and bypass future login requirements until a certain expiry. To prevent the risk of a request injecting these cookies manually and skipping OAuth freely, we require another secret to help derive an HMAC from the access token, host header, etc., ensuring cookie integrity.