Skip to content
Open
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
18 changes: 16 additions & 2 deletions doc/admin-guide/files/records.yaml.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5130,8 +5130,8 @@ removed in the future without prior notice.
.. ts:cv:: CONFIG proxy.config.quic.instance_id INT 0
:reloadable:

A static key used for calculating Stateless Reset Token. All instances in a
cluster need to share the same value.
An instance identifier mixed into Stateless Reset Tokens. All instances in a
cluster that share token keys need to use the same value.

.. ts:cv:: CONFIG proxy.config.quic.connection_table.size INT 65521

Expand All @@ -5146,6 +5146,20 @@ removed in the future without prior notice.

Enables Stateless Retry.

.. ts:cv:: CONFIG proxy.config.quic.server.token_key.filename STRING NULL
:reloadable:

The file containing the secret keys used to generate QUIC address-validation
and stateless-reset tokens. Relative paths are resolved from the |TS|
configuration directory. The file must contain one or more raw 32-byte keys.
The first key generates new tokens, while all keys validate address-validation
tokens to support key rotation. For example, generate a key with
``head -c32 /dev/urandom > quic_token.key``.

When this is not set, |TS| generates a random per-process key at startup. Set
the same key file on each server that must validate tokens generated by other
servers. Reload configuration after changing the key file.

.. ts:cv:: CONFIG proxy.config.quic.client.vn_exercise_enabled INT 0
:reloadable:

Expand Down
41 changes: 41 additions & 0 deletions include/iocore/net/quic/QUICConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,47 @@
#include "iocore/net/SSLTypes.h"
#include "mgmt/config/ConfigContext.h"

#include <array>
#include <string>
#include <vector>

class QUICTokenKeyConfigParams : public ConfigInfo
{
public:
static constexpr size_t KEY_LENGTH = 32;
using Key = std::array<uint8_t, KEY_LENGTH>;

~QUICTokenKeyConfigParams() override;

/** Load one or more raw token keys from @a path. */
bool load(const char *path, ConfigContext ctx = {});

/** Generate a random primary token key. */
bool generate(ConfigContext ctx = {});

const std::vector<Key> &keys() const;
const std::string &filename() const;

private:
std::vector<Key> m_keys;
std::string m_filename;
};

class QUICTokenKeyConfig
{
public:
static void startup();
static bool reconfigure(ConfigContext ctx = {});

static QUICTokenKeyConfigParams *acquire();
static void release(QUICTokenKeyConfigParams *params);

using scoped_config = ConfigProcessor::scoped_config<QUICTokenKeyConfig, QUICTokenKeyConfigParams>;

private:
static int _config_id;
};

class QUICConfigParams : public ConfigInfo
{
public:
Expand Down
13 changes: 10 additions & 3 deletions include/iocore/net/quic/QUICTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,13 +321,20 @@ class QUICStatelessResetToken
class QUICAddressValidationToken
{
public:
static constexpr size_t MAC_LENGTH = 32;

enum class Type : uint8_t {
RESUMPTION,
RETRY,
};

// FIXME Check token length
QUICAddressValidationToken(const uint8_t *buf, size_t len) : _token_len(len) { memcpy(this->_token, buf, len); }
QUICAddressValidationToken(const uint8_t *buf, size_t len)
{
if (buf != nullptr && len <= sizeof(_token)) {
memcpy(_token, buf, len);
_token_len = len;
}
}
virtual ~QUICAddressValidationToken(){};

static Type
Expand All @@ -354,7 +361,7 @@ class QUICAddressValidationToken

// The size should be smaller than maximum size of Retry packet
uint8_t _token[1200] = {0};
unsigned int _token_len;
unsigned int _token_len = 0;
};

class QUICResumptionToken : public QUICAddressValidationToken
Expand Down
3 changes: 3 additions & 0 deletions src/iocore/net/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ if(BUILD_TESTING)
unit_tests/unit_test_main.cc
unit_tests/benchmark_TLSCertCompression.cc
)
if(TS_USE_QUIC)
target_sources(test_net PRIVATE unit_tests/test_QUICTokenKeyConfig.cc)
endif()
# Use link groups to solve circular dependency
set(LINK_GROUP_LIBS
ts::logging
Expand Down
136 changes: 136 additions & 0 deletions src/iocore/net/quic/QUICConfig.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,155 @@

#include "iocore/net/quic/QUICConfig.h"

#include <openssl/crypto.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>

#include "mgmt/config/ConfigContextDiags.h"
#include "mgmt/config/ConfigRegistry.h"
#include "records/RecHttp.h"
#include "tscore/Layout.h"
#include "tscore/MatcherUtils.h"
#include "tscore/ink_memory.h"

#include "../P_SSLConfig.h"
#include "../P_TLSKeyLogger.h"

#include "iocore/net/quic/QUICGlobals.h"
#include "iocore/net/quic/QUICTransportParameters.h"

int QUICTokenKeyConfig::_config_id = 0;
int QUICConfig::_config_id = 0;
int QUICConfigParams::_connection_table_size = 65521;

QUICTokenKeyConfigParams::~QUICTokenKeyConfigParams()
{
if (!m_keys.empty()) {
OPENSSL_cleanse(m_keys.data(), m_keys.size() * sizeof(Key));
}
}

bool
QUICTokenKeyConfigParams::load(const char *path, ConfigContext ctx)
{
int key_data_len = 0;
ats_scoped_str key_data{readIntoBuffer(path, __func__, &key_data_len)};

if (!key_data) {
CfgLoadFail(ctx, "Could not load QUIC token key from %s", path);
return false;
}

if (key_data_len < static_cast<int>(KEY_LENGTH) || key_data_len % KEY_LENGTH != 0) {
CfgLoadFail(ctx, "QUIC token key file %s must contain one or more %zu-byte keys", path, KEY_LENGTH);
OPENSSL_cleanse(key_data.get(), key_data_len);
return false;
}

m_keys.resize(key_data_len / KEY_LENGTH);
memcpy(m_keys.data(), key_data.get(), key_data_len);
OPENSSL_cleanse(key_data.get(), key_data_len);
m_filename = path;
return true;
}

bool
QUICTokenKeyConfigParams::generate(ConfigContext ctx)
{
m_keys.resize(1);
if (RAND_bytes(m_keys.front().data(), static_cast<int>(m_keys.front().size())) != 1) {
CfgLoadFail(ctx, "Could not generate a random QUIC token key");
OPENSSL_cleanse(m_keys.data(), m_keys.size() * sizeof(Key));
m_keys.clear();
return false;
}
return true;
}

const std::vector<QUICTokenKeyConfigParams::Key> &
QUICTokenKeyConfigParams::keys() const
{
return m_keys;
}

const std::string &
QUICTokenKeyConfigParams::filename() const
{
return m_filename;
}

void
QUICTokenKeyConfig::startup()
{
config::ConfigRegistry::Get_Instance().register_record_config("quic_token_key",
[](ConfigContext ctx) {
CfgLoadLog(ctx, DL_Note, "QUIC token key loading ...");
if (QUICTokenKeyConfig::reconfigure(ctx)) {
ctx.complete("QUIC token key reloaded");
} else {
ctx.fail("Failed to reload QUIC token key");
}
},
{"proxy.config.quic.server.token_key.filename"});

if (!reconfigure()) {
Fatal("Failed to initialize QUIC token key");
}
}

bool
QUICTokenKeyConfig::reconfigure(ConfigContext ctx)
{
std::string path;
if (auto rec_str = RecGetRecordStringAlloc("proxy.config.quic.server.token_key.filename"); rec_str && !rec_str->empty()) {
path = Layout::relative_to(Layout::get()->sysconfdir, *rec_str);
}

if (path.empty()) {
bool already_random = false;
{
scoped_config current;
already_random = current && current->filename().empty();
}
if (already_random) {
return true;
}
}

auto *params = new QUICTokenKeyConfigParams;
if ((!path.empty() && !params->load(path.c_str(), ctx)) || (path.empty() && !params->generate(ctx))) {
delete params;
return false;
}

bool unchanged = false;
{
scoped_config current;
unchanged = current && current->filename() == params->filename() && current->keys() == params->keys();
}
if (unchanged) {
delete params;
return true;
}

_config_id = configProcessor.set(_config_id, params);
return true;
}

QUICTokenKeyConfigParams *
QUICTokenKeyConfig::acquire()
{
return static_cast<QUICTokenKeyConfigParams *>(configProcessor.get(_config_id));
}

void
QUICTokenKeyConfig::release(QUICTokenKeyConfigParams *params)
{
if (_config_id > 0) {
configProcessor.release(_config_id, params);
}
}

SSL_CTX *
quic_new_ssl_ctx()
{
Expand Down Expand Up @@ -448,6 +583,7 @@ QUICConfigParams::get_cc_algorithm() const
void
QUICConfig::startup()
{
QUICTokenKeyConfig::startup();
reconfigure();
}

Expand Down
Loading