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
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.34.6-dev
1.34.6
7 changes: 7 additions & 0 deletions changelogs/1.32.11.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
date: September 2, 2025

bug_fixes:
- area: oauth2
change: |
Fixed an issue where cookies prefixed with ``__Secure-`` or ``__Host-`` were not receiving a
Secure attribute (`CVE-2025-55162 <https://github.com/envoyproxy/envoy/security/advisories/GHSA-95j4-hw7f-v2rh>`_).
7 changes: 7 additions & 0 deletions changelogs/1.33.8.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
date: September 2, 2025

bug_fixes:
- area: oauth2
change: |
Fixed an issue where cookies prefixed with ``__Secure-`` or ``__Host-`` were not receiving a
Secure attribute.
16 changes: 1 addition & 15 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
date: Pending

behavior_changes:
# *Changes that are expected to cause an incompatibility if applicable; deployment changes are likely required*

minor_behavior_changes:
# *Changes that may cause incompatibilities for some users, but should not for most*
date: September 2, 2025

bug_fixes:
# *Changes expected to improve the state of the world and are unlikely to have negative effects*
- area: oauth2
change: |
Fixed an issue where cookies prefixed with ``__Secure-`` or ``__Host-`` were not receiving a
Expand All @@ -16,10 +9,3 @@ bug_fixes:
change: |
Fixed an UAF in DNS cache that can occur when the Host header is modified between the Dynamic Forwarding and Router
filters.

removed_config_or_runtime:
# *Normally occurs at the end of the* :ref:`deprecation period <deprecated>`

new_features:

deprecated:
Binary file modified docs/inventories/v1.32/objects.inv
Binary file not shown.
Binary file modified docs/inventories/v1.33/objects.inv
Binary file not shown.
Binary file modified docs/inventories/v1.34/objects.inv
Binary file not shown.
6 changes: 3 additions & 3 deletions docs/versions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@
"1.29": 1.29.12
"1.30": 1.30.11
"1.31": 1.31.10
"1.32": 1.32.10
"1.33": 1.33.7
"1.34": 1.34.4
"1.32": 1.32.11
"1.33": 1.33.8
"1.34": 1.34.5
1 change: 1 addition & 0 deletions source/common/quic/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ envoy_cc_library(
"//envoy/server:transport_socket_config_interface",
"//envoy/ssl:context_config_interface",
"//source/common/common:assert_lib",
"//source/common/network:raw_buffer_socket_lib",
"//source/common/network:transport_socket_options_lib",
"//source/common/tls:server_context_config_lib",
"//source/common/tls:server_context_lib",
Expand Down
177 changes: 174 additions & 3 deletions source/common/quic/envoy_quic_proof_source.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@

#include <openssl/bio.h>

#include <cstdlib>
#include <fstream>

#include "envoy/ssl/tls_certificate_config.h"

#include "source/common/quic/cert_compression.h"
#include "source/common/quic/envoy_quic_utils.h"
#include "source/common/quic/quic_io_handle_wrapper.h"
#include "source/common/runtime/runtime_features.h"
#include "source/common/stream_info/stream_info_impl.h"
#include "source/common/tls/context_config_impl.h"
#include "source/common/network/utility.h"

#include "openssl/bytestring.h"
#include "quiche/quic/core/crypto/certificate_view.h"
Expand All @@ -29,7 +34,7 @@ EnvoyQuicProofSource::GetCertChain(const quic::QuicSocketAddress& server_address
return nullptr;
}

return getTlsCertAndFilterChain(*res, hostname, cert_matched_sni).cert_;
return getTlsCertAndFilterChain(*res, hostname, cert_matched_sni, server_address, client_address).cert_;
}

void EnvoyQuicProofSource::signPayload(
Expand All @@ -44,7 +49,7 @@ void EnvoyQuicProofSource::signPayload(
}

CertWithFilterChain res =
getTlsCertAndFilterChain(*data, hostname, nullptr /* cert_matched_sni */);
getTlsCertAndFilterChain(*data, hostname, nullptr /* cert_matched_sni */, server_address, client_address);
if (res.private_key_ == nullptr) {
ENVOY_LOG(warn, "No matching filter chain found for handshake.");
callback->Run(false, "", nullptr);
Expand Down Expand Up @@ -74,13 +79,26 @@ void EnvoyQuicProofSource::signPayload(
EnvoyQuicProofSource::CertWithFilterChain
EnvoyQuicProofSource::getTlsCertAndFilterChain(const TransportSocketFactoryWithFilterChain& data,
const std::string& hostname,
bool* cert_matched_sni) {
bool* cert_matched_sni,
const quic::QuicSocketAddress& server_address,
const quic::QuicSocketAddress& client_address) {
auto [cert, key] =
data.transport_socket_factory_.getTlsCertificateAndKey(hostname, cert_matched_sni);
if (cert == nullptr || key == nullptr) {
ENVOY_LOG(warn, "No certificate is configured in transport socket config.");
return {};
}

// Cache the keylog configuration and connection info for this filter chain
try {
const auto& context_config = data.transport_socket_factory_.getContextConfig();
storeKeylogInfo(data.filter_chain_,
std::shared_ptr<const Ssl::ContextConfig>(&context_config, [](const Ssl::ContextConfig*){}),
server_address, client_address);
} catch (const std::exception& e) {
ENVOY_LOG(debug, "Failed to cache keylog info for filter chain: {}", e.what());
}

return {std::move(cert), std::move(key), data.filter_chain_};
}

Expand Down Expand Up @@ -117,6 +135,159 @@ void EnvoyQuicProofSource::updateFilterChainManager(

void EnvoyQuicProofSource::OnNewSslCtx(SSL_CTX* ssl_ctx) {
CertCompression::registerSslContext(ssl_ctx);

// Try to set up keylog callback for QUIC SSL contexts
setupQuicKeylogCallback(ssl_ctx);
}

void EnvoyQuicProofSource::setupQuicKeylogCallback(SSL_CTX* ssl_ctx) {
// Store reference to this proof source in SSL_CTX for use in keylog callback
SSL_CTX_set_app_data(ssl_ctx, this);

// Set up the keylog callback - the actual keylog configuration will be
// determined per-connection in the callback based on the filter chain
SSL_CTX_set_keylog_callback(ssl_ctx, quicKeylogCallback);
}

// Helper function to convert Envoy address to QUICHE address
quic::QuicSocketAddress envoyAddressToQuicAddress(const Network::Address::Instance& envoy_addr) {
if (envoy_addr.type() == Network::Address::Type::Ip) {
const auto& ip_addr = *envoy_addr.ip();
quiche::QuicheIpAddress quiche_addr;
if (quiche_addr.FromString(ip_addr.addressAsString())) {
return quic::QuicSocketAddress(quic::QuicIpAddress(quiche_addr), ip_addr.port());
}
}
// Return any address for non-IP addresses
return quic::QuicSocketAddress();
}

// Static keylog callback for QUIC SSL contexts
void EnvoyQuicProofSource::quicKeylogCallback(const SSL* ssl, const char* line) {
ASSERT(ssl != nullptr);

// Get the proof source instance from SSL_CTX
auto* proof_source =
static_cast<EnvoyQuicProofSource*>(SSL_CTX_get_app_data(SSL_get_SSL_CTX(ssl)));
ASSERT(proof_source != nullptr);

ENVOY_LOG(debug, "QUIC keylog callback invoked for line: {}", line);

// Try to find keylog configuration from cached filter chain information
// We iterate through all cached filter chains to find one with keylog configuration
bool keylog_written = false;
{
absl::MutexLock lock(&proof_source->keylog_cache_mutex_);
for (const auto& entry : proof_source->keylog_config_cache_) {
const auto& keylog_info = entry.second;
if (keylog_info.config) {
try {
// Convert QUIC addresses back to Envoy addresses for the bridge
std::string server_addr_str = absl::StrCat(
keylog_info.server_address.host().ToString(), ":",
keylog_info.server_address.port());
std::string client_addr_str = absl::StrCat(
keylog_info.client_address.host().ToString(), ":",
keylog_info.client_address.port());

Network::Address::InstanceConstSharedPtr local_addr =
Network::Utility::parseInternetAddressAndPortNoThrow(server_addr_str);
Network::Address::InstanceConstSharedPtr remote_addr =
Network::Utility::parseInternetAddressAndPortNoThrow(client_addr_str);

if (local_addr && remote_addr) {
QuicKeylogBridge::writeKeylog(*keylog_info.config, *local_addr, *remote_addr, line);
keylog_written = true;
ENVOY_LOG(debug, "QUIC keylog written using cached configuration");
break; // Successfully handled by built-in system
}
} catch (const std::exception& e) {
ENVOY_LOG(debug, "Failed to write keylog using cached config: {}", e.what());
}
}
}
}

if (keylog_written) {
return;
}

// Fallback: Use environment variable for backward compatibility
const char* keylog_path = std::getenv("SSLKEYLOGFILE");
if (keylog_path != nullptr) {
std::ofstream keylog_file(keylog_path, std::ios::app);
if (keylog_file.is_open()) {
keylog_file << line << "\n";
keylog_file.close();
ENVOY_LOG(debug, "QUIC keylog written to {}: {}", keylog_path, line);
}
}
}

void EnvoyQuicProofSource::QuicKeylogBridge::writeKeylog(
const Ssl::ContextConfig& config,
const Network::Address::Instance& local_addr,
const Network::Address::Instance& remote_addr,
const char* line) {

const std::string& keylog_path = config.tlsKeyLogPath();
if (keylog_path.empty()) {
return;
}

// Check address filtering
const auto& local_ip_list = config.tlsKeyLogLocal();
const auto& remote_ip_list = config.tlsKeyLogRemote();

bool local_match = (local_ip_list.getIpListSize() == 0 || local_ip_list.contains(local_addr));
bool remote_match = (remote_ip_list.getIpListSize() == 0 || remote_ip_list.contains(remote_addr));

if (!local_match || !remote_match) {
ENVOY_LOG(debug, "QUIC keylog filtered out by address match (local={}, remote={})",
local_match, remote_match);
return;
}

// Use access log manager to write keylog
try {
auto& access_log_manager = config.accessLogManager();
auto file_or_error = access_log_manager.createAccessLog(
Filesystem::FilePathAndType{Filesystem::DestinationType::File, keylog_path});

if (file_or_error.ok()) {
auto keylog_file = file_or_error.value();
keylog_file->write(absl::StrCat(line, "\n"));
ENVOY_LOG(debug, "QUIC keylog written via bridge to {}: {}", keylog_path, line);
} else {
ENVOY_LOG(warn, "Failed to create keylog file {}: {}", keylog_path,
file_or_error.status().message());
}
} catch (const std::exception& e) {
ENVOY_LOG(warn, "Failed to write QUIC keylog: {}", e.what());
}
}

// Get SSL socket index for storing transport socket callbacks
int EnvoyQuicProofSource::sslSocketIndex() {
static int ssl_socket_index = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
return ssl_socket_index;
}

void EnvoyQuicProofSource::storeKeylogInfo(const Network::FilterChain& filter_chain,
std::shared_ptr<const Ssl::ContextConfig> config,
const quic::QuicSocketAddress& server_address,
const quic::QuicSocketAddress& client_address) const {
absl::MutexLock lock(&keylog_cache_mutex_);
keylog_config_cache_[&filter_chain] = KeylogInfo{std::move(config), server_address, client_address};
}

absl::optional<EnvoyQuicProofSource::KeylogInfo> EnvoyQuicProofSource::getKeylogInfo(const Network::FilterChain& filter_chain) const {
absl::MutexLock lock(&keylog_cache_mutex_);
auto it = keylog_config_cache_.find(&filter_chain);
if (it != keylog_config_cache_.end()) {
return it->second;
}
return absl::nullopt;
}

} // namespace Quic
Expand Down
48 changes: 47 additions & 1 deletion source/common/quic/envoy_quic_proof_source.h
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
#pragma once

#include <unordered_map>

#include "envoy/ssl/context_config.h"

#include "source/common/common/thread.h"
#include "source/common/quic/envoy_quic_proof_source_base.h"
#include "source/common/quic/quic_server_transport_socket_factory.h"
#include "source/server/listener_stats.h"

#include "absl/synchronization/mutex.h"
#include "absl/types/optional.h"
#include "quiche/quic/platform/api/quic_socket_address.h"

namespace Envoy {
namespace Quic {

// A ProofSource implementation which supplies a proof instance with certs from filter chain.
class EnvoyQuicProofSource : public EnvoyQuicProofSourceBase {
public:
// Cache for keylog configurations by filter chain
struct KeylogInfo {
std::shared_ptr<const Ssl::ContextConfig> config;
quic::QuicSocketAddress server_address;
quic::QuicSocketAddress client_address;
};
EnvoyQuicProofSource(Network::Socket& listen_socket,
Network::FilterChainManager& filter_chain_manager,
Server::ListenerStats& listener_stats, TimeSource& time_source)
Expand All @@ -27,6 +42,15 @@ class EnvoyQuicProofSource : public EnvoyQuicProofSourceBase {

void updateFilterChainManager(Network::FilterChainManager& filter_chain_manager);

// Bridge interface for QUIC-TLS keylog integration
class QuicKeylogBridge {
public:
static void writeKeylog(const Ssl::ContextConfig& config,
const Network::Address::Instance& local_addr,
const Network::Address::Instance& remote_addr,
const char* line);
};

protected:
// quic::ProofSource
void signPayload(const quic::QuicSocketAddress& server_address,
Expand All @@ -47,17 +71,39 @@ class EnvoyQuicProofSource : public EnvoyQuicProofSourceBase {
};

CertWithFilterChain getTlsCertAndFilterChain(const TransportSocketFactoryWithFilterChain& data,
const std::string& hostname, bool* cert_matched_sni);
const std::string& hostname, bool* cert_matched_sni,
const quic::QuicSocketAddress& server_address,
const quic::QuicSocketAddress& client_address);

absl::optional<TransportSocketFactoryWithFilterChain>
getTransportSocketAndFilterChain(const quic::QuicSocketAddress& server_address,
const quic::QuicSocketAddress& client_address,
const std::string& hostname);

void setupQuicKeylogCallback(SSL_CTX* ssl_ctx);

// Static callback function for QUIC keylog
static void quicKeylogCallback(const SSL* ssl, const char* line);

// Get SSL socket index for storing transport socket callbacks
static int sslSocketIndex();

// Store keylog configuration and connection info for a filter chain
void storeKeylogInfo(const Network::FilterChain& filter_chain,
std::shared_ptr<const Ssl::ContextConfig> config,
const quic::QuicSocketAddress& server_address,
const quic::QuicSocketAddress& client_address) const;

// Get cached keylog information for a filter chain
absl::optional<KeylogInfo> getKeylogInfo(const Network::FilterChain& filter_chain) const;

Network::Socket& listen_socket_;
Network::FilterChainManager* filter_chain_manager_{nullptr};
Server::ListenerStats& listener_stats_;
TimeSource& time_source_;

mutable absl::Mutex keylog_cache_mutex_;
mutable std::unordered_map<const Network::FilterChain*, KeylogInfo> keylog_config_cache_ ABSL_GUARDED_BY(keylog_cache_mutex_);
};

} // namespace Quic
Expand Down
Loading