return d_tlsContext.rotateTicketsKey(now);
}
+void DOHFrontend::setTicketsKeyAddedHook(const dnsdist_tickets_key_added_hook& hook)
+{
+ return d_tlsContext.setTicketsKeyAddedHook(hook);
+}
+
void DOHFrontend::loadTicketsKeys(const std::string& keyFile)
{
return d_tlsContext.loadTicketsKeys(keyFile);
{
}
+ virtual void setTicketsKeyAddedHook(const dnsdist_tickets_key_added_hook& /* hook */)
+ {
+ }
+
virtual void loadTicketsKeys(const std::string& /* keyFile */)
{
}
virtual void setup();
virtual void reloadCertificates();
+ virtual void setTicketsKeyAddedHook(const dnsdist_tickets_key_added_hook& hook);
virtual void rotateTicketsKey(time_t now);
virtual void loadTicketsKeys(const std::string& keyFile);
virtual void handleTicketsKeyRotation();
}
});
+
+ luaCtx.registerFunction<void (std::shared_ptr<DOHFrontend>::*)(const dnsdist_tickets_key_added_hook&)>("setTicketsKeyAddedHook", [](const std::shared_ptr<DOHFrontend>& frontend, const dnsdist_tickets_key_added_hook& hook) {
+ if (frontend != nullptr) {
+ frontend->setTicketsKeyAddedHook(hook);
+ }
+ });
+
luaCtx.registerFunction<void (std::shared_ptr<DOHFrontend>::*)(const LuaArray<std::shared_ptr<DOHResponseMapEntry>>&)>("setResponsesMap", [](const std::shared_ptr<DOHFrontend>& frontend, const LuaArray<std::shared_ptr<DOHResponseMapEntry>>& map) {
if (frontend != nullptr) {
auto newMap = std::make_shared<std::vector<std::shared_ptr<DOHResponseMapEntry>>>();
}
});
+ luaCtx.registerFunction<void (std::shared_ptr<TLSCtx>::*)(const dnsdist_tickets_key_added_hook&)>("setTicketsKeyAddedHook", [](const std::shared_ptr<TLSCtx>& frontend, const dnsdist_tickets_key_added_hook& hook) {
+ if (frontend != nullptr) {
+ frontend->setTicketsKeyAddedHook(hook);
+ }
+ });
+
luaCtx.registerFunction<void (std::shared_ptr<TLSCtx>::*)(const std::string&)>("loadTicketsKeys", [](std::shared_ptr<TLSCtx>& ctx, const std::string& file) {
if (ctx != nullptr) {
ctx->loadTicketsKeys(file);
return frontend->d_addr.toStringWithPort();
});
+ luaCtx.registerFunction<void (std::shared_ptr<TLSFrontend>::*)(const dnsdist_tickets_key_added_hook&)>("setTicketsKeyAddedHook", [](const std::shared_ptr<TLSFrontend>& frontend, const dnsdist_tickets_key_added_hook& hook) {
+ if (frontend == nullptr) {
+ return;
+ }
+ auto ctx = frontend->getContext();
+ if (ctx) {
+ ctx->setTicketsKeyAddedHook(hook);
+ }
+ });
+
luaCtx.registerFunction<void (std::shared_ptr<TLSFrontend>::*)()>("rotateTicketsKey", [](std::shared_ptr<TLSFrontend>& frontend) {
if (frontend == nullptr) {
return;
Replace the current TLS tickets key by a new random one.
+ .. method:: DOHFrontend:setTicketsKeyAddedHook(callback)
+
+ .. versionadded:: 1.9.0
+
+ Set a Lua function that will be called everytime a new tickets key is added. The function receives:
+
+ * the key content as a string
+ * the keylen as an integer
+
+ See :doc:`../advanced/tls-sessions-management` for more information.
+
.. method:: DOHFrontend:setResponsesMap(rules)
Set a list of HTTP response rules allowing to intercept HTTP queries very early, before the DNS payload has been processed, and send custom responses including error pages, redirects and static content.
Replace the current TLS tickets key by a new random one.
+ .. method:: TLSContext:setTicketsKeyAddedHook(callback)
+
+ .. versionadded:: 1.9.0
+
+ Set a Lua function that will be called everytime a new tickets key is added. The function receives:
+
+ * the key content as a string
+ * the keylen as an integer
+
+ See :doc:`../advanced/tls-sessions-management` for more information.
+
TLSFrontend
~~~~~~~~~~~
Replace the current TLS tickets key by a new random one.
+ .. method:: TLSFrontend:setTicketsKeyAddedHook(callback)
+
+ .. versionadded:: 1.9.0
+
+ Set a Lua function that will be called everytime a new tickets key is added. The function receives:
+
+ * the key content as a string
+ * the keylen as an integer
+
+ See :doc:`../advanced/tls-sessions-management` for more information.
+
EDNS on Self-generated answers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void OpenSSLTLSTicketKeysRing::addKey(std::shared_ptr<OpenSSLTLSTicketKey>&& newKey)
{
d_ticketKeys.write_lock()->push_front(std::move(newKey));
+ if (d_ticketsKeyAddedHook) {
+ auto key = d_ticketKeys.read_lock()->front();
+ auto keyContent = key->content();
+ d_ticketsKeyAddedHook(keyContent.c_str(), keyContent.size());
+ }
+}
+
+void OpenSSLTLSTicketKeysRing::setTicketsKeyAddedHook(const dnsdist_tickets_key_added_hook& hook)
+{
+ d_ticketsKeyAddedHook = hook;
}
std::shared_ptr<OpenSSLTLSTicketKey> OpenSSLTLSTicketKeysRing::getEncryptionKey()
return (memcmp(d_name, name, sizeof(d_name)) == 0);
}
+std::string OpenSSLTLSTicketKey::content() const
+{
+ std::string result{};
+ result.reserve(TLS_TICKETS_KEY_NAME_SIZE + TLS_TICKETS_CIPHER_KEY_SIZE + TLS_TICKETS_MAC_KEY_SIZE);
+ result.append(reinterpret_cast<const char*>(d_name), TLS_TICKETS_KEY_NAME_SIZE);
+ result.append(reinterpret_cast<const char*>(d_cipherKey), TLS_TICKETS_CIPHER_KEY_SIZE);
+ result.append(reinterpret_cast<const char*>(d_hmacKey), TLS_TICKETS_MAC_KEY_SIZE);
+
+ return result;
+}
+
#if OPENSSL_VERSION_MAJOR >= 3
static const std::string sha256KeyName{"sha256"};
#endif
#if OPENSSL_VERSION_MAJOR >= 3
int encrypt(unsigned char keyName[TLS_TICKETS_KEY_NAME_SIZE], unsigned char* iv, EVP_CIPHER_CTX* ectx, EVP_MAC_CTX* hctx) const;
bool decrypt(const unsigned char* iv, EVP_CIPHER_CTX* ectx, EVP_MAC_CTX* hctx) const;
+ std::string content() const;
#else
int encrypt(unsigned char keyName[TLS_TICKETS_KEY_NAME_SIZE], unsigned char* iv, EVP_CIPHER_CTX* ectx, HMAC_CTX* hctx) const;
bool decrypt(const unsigned char* iv, EVP_CIPHER_CTX* ectx, HMAC_CTX* hctx) const;
unsigned char d_hmacKey[TLS_TICKETS_MAC_KEY_SIZE];
};
+using dnsdist_tickets_key_added_hook = std::function<void(const char* key, size_t keyLen)>;
+
class OpenSSLTLSTicketKeysRing
{
public:
size_t getKeysCount();
void loadTicketsKeys(const std::string& keyFile);
void rotateTicketsKey(time_t now);
+ void setTicketsKeyAddedHook(const dnsdist_tickets_key_added_hook& hook);
private:
void addKey(std::shared_ptr<OpenSSLTLSTicketKey>&& newKey);
-
+ dnsdist_tickets_key_added_hook d_ticketsKeyAddedHook;
SharedLockGuarded<boost::circular_buffer<std::shared_ptr<OpenSSLTLSTicketKey> > > d_ticketKeys;
};
}
}
+ void setTicketsKeyAddedHook(const dnsdist_tickets_key_added_hook& hook) override
+ {
+ d_feContext->d_ticketKeys.setTicketsKeyAddedHook(hook);
+ }
+
void loadTicketsKeys(const std::string& keyFile) final
{
d_feContext->d_ticketKeys.loadTicketsKeys(keyFile);
throw;
}
}
+ std::string content() const
+ {
+ std::string result{};
+ if (d_key.data != nullptr && d_key.size > 0) {
+ result.append(reinterpret_cast<const char*>(d_key.data), d_key.size);
+ }
+ return result;
+ }
~GnuTLSTicketsKey()
{
return connection;
}
+ void setTicketsKeyAddedHook(const dnsdist_tickets_key_added_hook& hook) override
+ {
+ d_ticketsKeyAddedHook = hook;
+ }
+
void rotateTicketsKey(time_t now) override
{
if (!d_enableTickets) {
if (d_ticketsKeyRotationDelay > 0) {
d_ticketsKeyNextRotation = now + d_ticketsKeyRotationDelay;
}
+
+ if (d_ticketsKeyAddedHook) {
+ auto ticketsKey = *(d_ticketsKey.read_lock());
+ auto content = ticketsKey->content();
+ d_ticketsKeyAddedHook(content.c_str(), content.size());
+ }
}
void loadTicketsKeys(const std::string& file) final
SharedLockGuarded<std::shared_ptr<GnuTLSTicketsKey>> d_ticketsKey{nullptr};
bool d_enableTickets{true};
bool d_validateCerts{true};
+ dnsdist_tickets_key_added_hook d_ticketsKeyAddedHook;
};
#endif /* HAVE_GNUTLS */
{
throw std::runtime_error("This TLS backend does not have the capability to load a tickets key from a file");
}
+ virtual void setTicketsKeyAddedHook(const dnsdist_tickets_key_added_hook& /* hook */)
+ {
+ throw std::runtime_error("This TLS backend does not have the capability to setup a hook for added tickets keys");
+ }
void handleTicketsKeyRotation(time_t now)
{
}
}
+ void setTicketsKeyAddedHook(const dnsdist_tickets_key_added_hook& hook)
+ {
+ if (d_ctx != nullptr) {
+ d_ctx->setTicketsKeyAddedHook(hook);
+ }
+ }
+
void loadTicketsKeys(const std::string& file)
{
if (d_ctx != nullptr) {
cls.startResponders()
cls.startDNSDist()
cls.setUpSockets()
+
+class TestTLSTicketsKeyAddedCallback(DNSDistTest):
+ _consoleKey = DNSDistTest.generateConsoleKey()
+ _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+
+ _serverKey = 'server.key'
+ _serverCert = 'server.chain'
+ _serverName = 'tls.tests.dnsdist.org'
+ _caCert = 'ca.pem'
+ _tlsServerPort = pickAvailablePort()
+ _numberOfKeys = 5
+
+ _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_tlsServerPort', '_serverCert', '_serverKey']
+ _config_template = """
+ setKey("%s")
+ controlSocket("127.0.0.1:%s")
+
+ newServer{address="127.0.0.1:%s"}
+ addTLSLocal("127.0.0.1:%s", "%s", "%s", { provider="openssl" })
+
+ callbackCalled = 0
+ function keyAddedCallback(key, keyLen)
+ callbackCalled = keyLen
+ end
+
+ """
+
+ def testLuaThreadCounter(self):
+ """
+ LuaThread: Test the lua newThread interface
+ """
+ self.sendConsoleCommand('getTLSFrontend(0):setTicketsKeyAddedHook(keyAddedCallback)');
+ called = self.sendConsoleCommand('callbackCalled')
+ self.assertEqual(int(called), 0)
+ self.sendConsoleCommand("getTLSFrontend(0):rotateTicketsKey()")
+ called = self.sendConsoleCommand('callbackCalled')
+ self.assertGreater(int(called), 0)