Skip to content
Closed
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
4 changes: 2 additions & 2 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ git_repository(
)

# When updating envoy sha manually please update the sha in istio.deps file also
ENVOY_SHA = "2b2c299144600fb9e525d21aabf39bf48e64fb1f"
ENVOY_SHA = "a9bcb26375658a505e19938f4816a92d6e338053"

http_archive(
name = "envoy",
strip_prefix = "envoy-" + ENVOY_SHA,
url = "https://github.com/envoyproxy/envoy/archive/" + ENVOY_SHA + ".zip",
url = "https://github.com/thalesignite/envoy/archive/" + ENVOY_SHA + ".zip",
)

load("@envoy//bazel:repositories.bzl", "envoy_dependencies")
Expand Down
2 changes: 1 addition & 1 deletion istio.deps
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
"name": "ENVOY_SHA",
"repoName": "envoyproxy/envoy",
"file": "WORKSPACE",
"lastStableSHA": "2b2c299144600fb9e525d21aabf39bf48e64fb1f"
"lastStableSHA": "2eb5bd5acc11a88f7c284ebc0eb7d40c27a93e2d"
}
]
2 changes: 1 addition & 1 deletion src/envoy/http/authn/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ envoy_cc_library(
repository = "@envoy",
deps = [
"//external:authentication_policy_config_cc_proto",
"//src/envoy/http/jwt_auth:jwt_lib",
"//src/envoy/utils:jwt_lib",
"//src/envoy/utils:utils_lib",
"//src/istio/authn:context_proto",
],
Expand Down
4 changes: 2 additions & 2 deletions src/envoy/http/authn/authn_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

#include "authn_utils.h"
#include "common/json/json_loader.h"
#include "src/envoy/http/jwt_auth/jwt.h"
#include "src/envoy/utils/jwt.h"

namespace Envoy {
namespace Http {
Expand Down Expand Up @@ -61,7 +61,7 @@ bool AuthnUtils::GetJWTPayloadFromHeaders(
}
std::string value(entry->value().c_str(), entry->value().size());
// JwtAuth::Base64UrlDecode() is different from Base64::decode().
std::string payload_str = JwtAuth::Base64UrlDecode(value);
std::string payload_str = Utils::Jwt::Base64UrlDecode(value);
// Return an empty string if Base64 decode fails.
if (payload_str.empty()) {
ENVOY_LOG(error, "Invalid {} header, invalid base64: {}",
Expand Down
94 changes: 15 additions & 79 deletions src/envoy/http/jwt_auth/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -24,40 +24,6 @@ load(
"envoy_cc_test",
)

envoy_cc_library(
name = "jwt_lib",
srcs = ["jwt.cc"],
hdrs = ["jwt.h"],
external_deps = [
"rapidjson",
"ssl",
],
repository = "@envoy",
deps = [
"@envoy//source/exe:envoy_common_lib",
],
)

envoy_cc_library(
name = "jwt_authenticator_lib",
srcs = [
"jwt_authenticator.cc",
"token_extractor.cc",
],
hdrs = [
"auth_store.h",
"jwt_authenticator.h",
"pubkey_cache.h",
"token_extractor.h",
],
repository = "@envoy",
deps = [
":jwt_lib",
"@envoy_api//envoy/config/filter/http/jwt_authn/v2alpha:jwt_authn_cc",
"@envoy//source/exe:envoy_common_lib",
],
)

envoy_cc_library(
name = "http_filter_lib",
srcs = [
Expand All @@ -68,7 +34,8 @@ envoy_cc_library(
],
repository = "@envoy",
deps = [
":jwt_authenticator_lib",
"//src/envoy/utils:jwt_authenticator_lib",
"//src/envoy/utils:utils_lib",
"@envoy//source/exe:envoy_common_lib",
],
)
Expand All @@ -83,6 +50,17 @@ envoy_cc_library(
],
)

envoy_cc_test(
name = "http_filter_test",
srcs = ["http_filter_test.cc"],
repository = "@envoy",
deps = [
":http_filter_lib",
"@envoy//test/test_common:utility_lib",
"@envoy//test/mocks/http:http_mocks",
],
)

envoy_cc_test(
name = "http_filter_integration_test",
srcs = [":integration_test/http_filter_integration_test.cc"],
Expand All @@ -93,51 +71,9 @@ envoy_cc_test(
repository = "@envoy",
deps = [
":http_filter_factory",
":jwt_lib",
"//src/envoy/utils:jwt_lib",
"//src/envoy/utils:jwt_authenticator_lib",
"@envoy//test/integration:http_integration_lib",
"@envoy//test/integration:integration_lib",
],
)

envoy_cc_test(
name = "jwt_test",
srcs = [
"jwt_test.cc",
],
data = [],
repository = "@envoy",
deps = [
":jwt_lib",
"@envoy//source/exe:envoy_common_lib",
"@envoy//test/test_common:utility_lib",
],
)

envoy_cc_test(
name = "jwt_authenticator_test",
srcs = [
"jwt_authenticator_test.cc",
],
data = [],
repository = "@envoy",
deps = [
":jwt_authenticator_lib",
"@envoy//source/exe:envoy_common_lib",
"@envoy//test/mocks/upstream:upstream_mocks",
"@envoy//test/test_common:utility_lib",
],
)

envoy_cc_test(
name = "token_extractor_test",
srcs = [
"token_extractor_test.cc",
],
data = [],
repository = "@envoy",
deps = [
":jwt_authenticator_lib",
"@envoy//source/exe:envoy_common_lib",
"@envoy//test/test_common:utility_lib",
],
)
95 changes: 70 additions & 25 deletions src/envoy/http/jwt_auth/http_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
*/

#include "src/envoy/http/jwt_auth/http_filter.h"
#include "src/envoy/utils/constants.h"

#include "common/http/message_impl.h"
#include "common/http/codes.h"
#include "common/http/utility.h"
#include "envoy/http/async_client.h"

Expand All @@ -25,72 +27,110 @@
namespace Envoy {
namespace Http {

JwtVerificationFilter::JwtVerificationFilter(Upstream::ClusterManager& cm,
JwtAuth::JwtAuthStore& store)
: jwt_auth_(cm, store) {}
JwtVerificationFilter::JwtVerificationFilter(std::shared_ptr<Utils::Jwt::JwtAuthenticator> jwt_auth,
const ::envoy::config::filter::http::jwt_authn::v2alpha::JwtAuthentication& config)
: jwt_auth_(jwt_auth), config_(config) {}

JwtVerificationFilter::~JwtVerificationFilter() {}

void JwtVerificationFilter::onDestroy() {
ENVOY_LOG(debug, "Called JwtVerificationFilter : {}", __func__);
jwt_auth_.onDestroy();
ENVOY_LOG(trace, "Called JwtVerificationFilter : {}", __func__);
jwt_auth_->onDestroy();
stream_reset_ = true;
}

FilterHeadersStatus JwtVerificationFilter::decodeHeaders(HeaderMap& headers,
bool) {
ENVOY_LOG(debug, "Called JwtVerificationFilter : {}", __func__);
ENVOY_LOG(trace, "Called JwtVerificationFilter : {}", __func__);
state_ = Calling;
stopped_ = false;
stream_reset_ = false;
headers_ = &headers;

// Sanitize the JWT verification result in the HTTP headers
// TODO (lei-tang): when the JWT verification result is in a configurable
// header, need to sanitize based on the configuration.
headers.remove(JwtAuth::JwtAuthenticator::JwtPayloadKey());
headers.remove(Utils::Constants::JwtPayloadKey());

// Verify the JWT token, onDone() will be called when completed.
jwt_auth_.Verify(headers, this);
jwt_auth_->Verify(headers, this);

if (state_ == Complete) {
ENVOY_LOG(trace, "Called JwtVerificationFilter : {} Complete", __func__);
return FilterHeadersStatus::Continue;
}
ENVOY_LOG(debug, "Called JwtVerificationFilter : {} Stop", __func__);
ENVOY_LOG(trace, "Called JwtVerificationFilter : {} Stop", __func__);
stopped_ = true;
return FilterHeadersStatus::StopIteration;
}

void JwtVerificationFilter::onDone(const JwtAuth::Status& status) {
ENVOY_LOG(debug, "Called JwtVerificationFilter : check complete {}",
int(status));
void JwtVerificationFilter::onSuccess(const Utils::Jwt::Jwt *jwt, const Http::LowerCaseString *header) {
ENVOY_LOG(trace, "Called JwtVerificationFilter : onSuccess {}");
// This stream has been reset, abort the callback.
if (state_ == Responded) {
return;
}
if (status != JwtAuth::Status::OK) {
state_ = Responded;
// verification failed
Code code = Code(401); // Unauthorized
// return failure reason as message body
Utility::sendLocalReply(*decoder_callbacks_, false, code,
JwtAuth::StatusToString(status));
return;
}

state_ = Complete;
// TODO(lei-tang): remove this backward compatibility.
// Tracking issue: https://github.com/istio/istio/issues/4744
headers_->addReferenceKey(Utils::Constants::JwtPayloadKey(), jwt->PayloadStrBase64Url());
// Use the issuer field of the JWT to lookup forwarding rules.
auto rules = config_.rules();
auto rule = rules[jwt->Iss()];
if (rule.has_forwarder()) {
auto forwarding_rule = rule.forwarder();
if (!forwarding_rule.forward_payload_header().empty()) {
const Http::LowerCaseString key(
forwarding_rule.forward_payload_header());
if (key.get() != Utils::Constants::JwtPayloadKey().get()) {
headers_->addCopy(key, jwt->PayloadStrBase64Url());
}
}
if (!forwarding_rule.forward() && header) {
// Remove JWT from headers.
headers_->remove(*header);
}
}
if (stopped_) {
decoder_callbacks_->continueDecoding();
}
}

void JwtVerificationFilter::onError(Utils::Jwt::Status status) {
// This stream has been reset, abort the callback.
ENVOY_LOG(trace, "Called JwtVerificationFilter : check onError {}",
int(status));
if (state_ != Calling) {
return;
}
// Check if verification can be bypassed. If not error out.
if (!OkToBypass()) {
state_ = Responded;
// verification failed
Code code = Code(401); // Unauthorized
// Log failure reason but do not return in reply as we do not want to inadvertently leak potentially sensitive
// JWT authentication configuration to an attacker.
ENVOY_LOG(info, "JWT authentication failed: {}", Utils::Jwt::StatusToString(status));
Utility::sendLocalReply(*decoder_callbacks_, stream_reset_, code, CodeUtility::toString(code));
} else {
ENVOY_LOG(debug, "Bypassing failed jwt authentication as defined by the jwt-auth filter's configuration.");
state_ = Complete;
if (stopped_) {
decoder_callbacks_->continueDecoding();
}
}
}

FilterDataStatus JwtVerificationFilter::decodeData(Buffer::Instance&, bool) {
ENVOY_LOG(debug, "Called JwtVerificationFilter : {}", __func__);
ENVOY_LOG(trace, "Called JwtVerificationFilter : {}", __func__);
if (state_ == Calling) {
return FilterDataStatus::StopIterationAndWatermark;
}
return FilterDataStatus::Continue;
}

FilterTrailersStatus JwtVerificationFilter::decodeTrailers(HeaderMap&) {
ENVOY_LOG(debug, "Called JwtVerificationFilter : {}", __func__);
ENVOY_LOG(trace, "Called JwtVerificationFilter : {}", __func__);
if (state_ == Calling) {
return FilterTrailersStatus::StopIteration;
}
Expand All @@ -99,9 +139,14 @@ FilterTrailersStatus JwtVerificationFilter::decodeTrailers(HeaderMap&) {

void JwtVerificationFilter::setDecoderFilterCallbacks(
StreamDecoderFilterCallbacks& callbacks) {
ENVOY_LOG(debug, "Called JwtVerificationFilter : {}", __func__);
ENVOY_LOG(trace, "Called JwtVerificationFilter : {}", __func__);
decoder_callbacks_ = &callbacks;
}

bool JwtVerificationFilter::OkToBypass() const {
// TODO: Use bypass field.
return config_.allow_missing_or_failed();
}

} // namespace Http
} // namespace Envoy
28 changes: 20 additions & 8 deletions src/envoy/http/jwt_auth/http_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
*/

#pragma once
#include "envoy/config/filter/http/jwt_authn/v2alpha/config.pb.h"

#include "src/envoy/http/jwt_auth/jwt_authenticator.h"
#include "src/envoy/utils/jwt_authenticator.h"

#include "common/common/logger.h"
#include "envoy/http/filter.h"
Expand All @@ -25,11 +26,11 @@ namespace Http {

// The Envoy filter to process JWT auth.
class JwtVerificationFilter : public StreamDecoderFilter,
public JwtAuth::JwtAuthenticator::Callbacks,
public Utils::Jwt::JwtAuthenticator::Callbacks,
public Logger::Loggable<Logger::Id::filter> {
public:
JwtVerificationFilter(Upstream::ClusterManager& cm,
JwtAuth::JwtAuthStore& store);
JwtVerificationFilter(std::shared_ptr<Utils::Jwt::JwtAuthenticator> jwt_auth,
const ::envoy::config::filter::http::jwt_authn::v2alpha::JwtAuthentication& config);
~JwtVerificationFilter();

// Http::StreamFilterBase
Expand All @@ -43,19 +44,30 @@ class JwtVerificationFilter : public StreamDecoderFilter,
StreamDecoderFilterCallbacks& callbacks) override;

private:
// the function for JwtAuth::Authenticator::Callbacks interface.
// To be called when its Verify() call is completed.
void onDone(const JwtAuth::Status& status) override;
// JwtAuth::Authenticator::Callbacks interface.
// To be called when its Verify() call is completed successfully.
void onSuccess(const Utils::Jwt::Jwt *jwt, const Http::LowerCaseString *header) override;
// To be called when token authentication fails
void onError(Utils::Jwt::Status status) override;

// The callback funcion.
StreamDecoderFilterCallbacks* decoder_callbacks_;
// The auth object.
JwtAuth::JwtAuthenticator jwt_auth_;
std::shared_ptr<Utils::Jwt::JwtAuthenticator> jwt_auth_;
// The filter configuration.
const ::envoy::config::filter::http::jwt_authn::v2alpha::JwtAuthentication config_;
// The state of the request
enum State { Init, Calling, Responded, Complete };
State state_ = Init;
// Mark if request has been stopped.
bool stopped_ = false;
// Stream has been reset.
bool stream_reset_;

// The HTTP request headers
Http::HeaderMap* headers_{};

bool OkToBypass() const;
};

} // namespace Http
Expand Down
Loading