mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-01 22:45:18 +00:00
[trac999] apply query ACL for incoming queries
This commit is contained in:
@@ -155,6 +155,14 @@ public:
|
|||||||
OutputBufferPtr buffer,
|
OutputBufferPtr buffer,
|
||||||
DNSServer* server);
|
DNSServer* server);
|
||||||
|
|
||||||
|
const Resolver::ClientACL& getQueryACL() const {
|
||||||
|
return (*query_acl_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setQueryACL(shared_ptr<const Resolver::ClientACL> new_acl) {
|
||||||
|
query_acl_ = new_acl;
|
||||||
|
}
|
||||||
|
|
||||||
/// Currently non-configurable, but will be.
|
/// Currently non-configurable, but will be.
|
||||||
static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
|
static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
|
||||||
|
|
||||||
@@ -177,11 +185,10 @@ public:
|
|||||||
/// Number of retries after timeout
|
/// Number of retries after timeout
|
||||||
unsigned retries_;
|
unsigned retries_;
|
||||||
|
|
||||||
|
private:
|
||||||
/// TBD
|
/// TBD
|
||||||
shared_ptr<const Resolver::ClientACL> query_acl_;
|
shared_ptr<const Resolver::ClientACL> query_acl_;
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/// Object to handle upstream queries
|
/// Object to handle upstream queries
|
||||||
RecursiveQuery* rec_query_;
|
RecursiveQuery* rec_query_;
|
||||||
};
|
};
|
||||||
@@ -453,25 +460,44 @@ Resolver::processMessage(const IOMessage& io_message,
|
|||||||
buffer, Rcode::NOTAUTH());
|
buffer, Rcode::NOTAUTH());
|
||||||
// Notify arrived, but we are not authoritative.
|
// Notify arrived, but we are not authoritative.
|
||||||
LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_NFYNOTAUTH);
|
LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_NFYNOTAUTH);
|
||||||
|
|
||||||
} else if (query_message->getOpcode() != Opcode::QUERY()) {
|
} else if (query_message->getOpcode() != Opcode::QUERY()) {
|
||||||
|
|
||||||
// Unsupported opcode.
|
// Unsupported opcode.
|
||||||
LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_OPCODEUNS)
|
LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_OPCODEUNS)
|
||||||
.arg(query_message->getOpcode());
|
.arg(query_message->getOpcode());
|
||||||
makeErrorMessage(query_message, answer_message,
|
makeErrorMessage(query_message, answer_message,
|
||||||
buffer, Rcode::NOTIMP());
|
buffer, Rcode::NOTIMP());
|
||||||
|
|
||||||
} else if (query_message->getRRCount(Message::SECTION_QUESTION) != 1) {
|
} else if (query_message->getRRCount(Message::SECTION_QUESTION) != 1) {
|
||||||
|
|
||||||
// Not one question
|
// Not one question
|
||||||
LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_NOTONEQUES)
|
LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_NOTONEQUES)
|
||||||
.arg(query_message->getRRCount(Message::SECTION_QUESTION));
|
.arg(query_message->getRRCount(Message::SECTION_QUESTION));
|
||||||
makeErrorMessage(query_message, answer_message,
|
makeErrorMessage(query_message, answer_message,
|
||||||
buffer, Rcode::FORMERR());
|
buffer, Rcode::FORMERR());
|
||||||
} else {
|
} else {
|
||||||
ConstQuestionPtr question = *query_message->beginQuestion();
|
const ConstQuestionPtr question = *query_message->beginQuestion();
|
||||||
const RRType &qtype = question->getType();
|
const RRType qtype = question->getType();
|
||||||
|
|
||||||
|
Client client(io_message);
|
||||||
|
const BasicAction query_action(impl_->getQueryACL().execute(client));
|
||||||
|
if (query_action == REJECT) {
|
||||||
|
LOG_INFO(resolver_logger, RESOLVER_QUERYREJECTED)
|
||||||
|
.arg(question->getName()).arg(qtype).arg(question->getClass())
|
||||||
|
.arg(client);
|
||||||
|
makeErrorMessage(query_message, answer_message, buffer,
|
||||||
|
Rcode::REFUSED());
|
||||||
|
// the following should be refactored
|
||||||
|
server->resume(true);
|
||||||
|
return;
|
||||||
|
} else if (query_action == DROP) {
|
||||||
|
LOG_INFO(resolver_logger, RESOLVER_QUERYDROPPED)
|
||||||
|
.arg(question->getName()).arg(qtype).arg(question->getClass())
|
||||||
|
.arg(client);
|
||||||
|
server->resume(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO, RESOLVER_QUERYACCEPTED)
|
||||||
|
.arg(question->getName()).arg(qtype).arg(question->getClass())
|
||||||
|
.arg(client);
|
||||||
|
|
||||||
if (qtype == RRType::AXFR()) {
|
if (qtype == RRType::AXFR()) {
|
||||||
if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
|
if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
|
||||||
|
|
||||||
@@ -481,7 +507,6 @@ Resolver::processMessage(const IOMessage& io_message,
|
|||||||
makeErrorMessage(query_message, answer_message,
|
makeErrorMessage(query_message, answer_message,
|
||||||
buffer, Rcode::FORMERR());
|
buffer, Rcode::FORMERR());
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// ... or over TCP for that matter
|
// ... or over TCP for that matter
|
||||||
LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS,
|
LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS,
|
||||||
RESOLVER_AXFRTCP);
|
RESOLVER_AXFRTCP);
|
||||||
@@ -489,12 +514,10 @@ Resolver::processMessage(const IOMessage& io_message,
|
|||||||
buffer, Rcode::NOTIMP());
|
buffer, Rcode::NOTIMP());
|
||||||
}
|
}
|
||||||
} else if (qtype == RRType::IXFR()) {
|
} else if (qtype == RRType::IXFR()) {
|
||||||
|
|
||||||
// Can't process IXFR request
|
// Can't process IXFR request
|
||||||
LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_IXFR);
|
LOG_DEBUG(resolver_logger, RESOLVER_DBG_PROCESS, RESOLVER_IXFR);
|
||||||
makeErrorMessage(query_message, answer_message,
|
makeErrorMessage(query_message, answer_message,
|
||||||
buffer, Rcode::NOTIMP());
|
buffer, Rcode::NOTIMP());
|
||||||
|
|
||||||
} else if (question->getClass() != RRClass::IN()) {
|
} else if (question->getClass() != RRClass::IN()) {
|
||||||
|
|
||||||
// Non-IN message received, refuse it.
|
// Non-IN message received, refuse it.
|
||||||
@@ -754,11 +777,11 @@ Resolver::getListenAddresses() const {
|
|||||||
|
|
||||||
const Resolver::ClientACL&
|
const Resolver::ClientACL&
|
||||||
Resolver::getQueryACL() const {
|
Resolver::getQueryACL() const {
|
||||||
return (*impl_->query_acl_);
|
return (impl_->getQueryACL());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Resolver::setQueryACL(shared_ptr<const ClientACL> new_acl) {
|
Resolver::setQueryACL(shared_ptr<const ClientACL> new_acl) {
|
||||||
LOG_INFO(resolver_logger, RESOLVER_SETQUERYACL);
|
LOG_INFO(resolver_logger, RESOLVER_SETQUERYACL);
|
||||||
impl_->query_acl_ = new_acl;
|
impl_->setQueryACL(new_acl);
|
||||||
}
|
}
|
||||||
|
@@ -195,3 +195,12 @@ query and is ignoring it.
|
|||||||
% SETQUERYACL query ACL is configured
|
% SETQUERYACL query ACL is configured
|
||||||
A debug message that appears when a new query ACL is configured for the
|
A debug message that appears when a new query ACL is configured for the
|
||||||
resolver.
|
resolver.
|
||||||
|
|
||||||
|
%QUERYREJECTED query rejected: '%1/%2/%3' from %4
|
||||||
|
TBD
|
||||||
|
|
||||||
|
%QUERYDROPPED query dropped: '%1/%2/%3' from %4
|
||||||
|
TBD
|
||||||
|
|
||||||
|
%QUERYACCEPTED query accepted: '%1/%2/%3' from %4
|
||||||
|
TBD
|
||||||
|
@@ -12,14 +12,19 @@
|
|||||||
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <dns/name.h>
|
#include <dns/name.h>
|
||||||
|
|
||||||
|
#include <cc/data.h>
|
||||||
#include <resolver/resolver.h>
|
#include <resolver/resolver.h>
|
||||||
#include <dns/tests/unittest_util.h>
|
#include <dns/tests/unittest_util.h>
|
||||||
#include <testutils/dnsmessage_test.h>
|
#include <testutils/dnsmessage_test.h>
|
||||||
#include <testutils/srv_test.h>
|
#include <testutils/srv_test.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
using namespace isc::dns;
|
using namespace isc::dns;
|
||||||
|
using namespace isc::data;
|
||||||
using namespace isc::testutils;
|
using namespace isc::testutils;
|
||||||
using isc::UnitTestUtil;
|
using isc::UnitTestUtil;
|
||||||
|
|
||||||
@@ -28,7 +33,17 @@ const char* const TEST_PORT = "53535";
|
|||||||
|
|
||||||
class ResolverTest : public SrvTestBase{
|
class ResolverTest : public SrvTestBase{
|
||||||
protected:
|
protected:
|
||||||
ResolverTest() : server(){}
|
ResolverTest() : server() {
|
||||||
|
// By default queries from the "default remote address" will be
|
||||||
|
// rejected, so we'll need to add an explicit ACL entry to allow that.
|
||||||
|
server.setConfigured();
|
||||||
|
server.updateConfig(Element::fromJSON(
|
||||||
|
"{ \"query_acl\": "
|
||||||
|
" [ {\"action\": \"ACCEPT\","
|
||||||
|
" \"from\": \"" +
|
||||||
|
string(DEFAULT_REMOTE_ADDRESS) +
|
||||||
|
"\"} ] }"));
|
||||||
|
}
|
||||||
virtual void processMessage() {
|
virtual void processMessage() {
|
||||||
server.processMessage(*io_message,
|
server.processMessage(*io_message,
|
||||||
parse_message,
|
parse_message,
|
||||||
@@ -136,4 +151,38 @@ TEST_F(ResolverTest, notifyFail) {
|
|||||||
Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
|
Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverTest, queryACL) {
|
||||||
|
// The "ACCEPT" cases are covered in other tests. Here we explicitly
|
||||||
|
// test "REJECT" and "DROP" cases.
|
||||||
|
|
||||||
|
// Clear the existing ACL, reverting to the "default reject" rule.
|
||||||
|
|
||||||
|
// AXFR over UDP. This would otherwise result in FORMERR.
|
||||||
|
server.updateConfig(Element::fromJSON("{ \"query_acl\": [] }"));
|
||||||
|
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
|
||||||
|
Name("example.com"), RRClass::IN(),
|
||||||
|
RRType::AXFR());
|
||||||
|
createRequestPacket(request_message, IPPROTO_UDP);
|
||||||
|
server.processMessage(*io_message, parse_message, response_message,
|
||||||
|
response_obuffer, &dnsserv);
|
||||||
|
EXPECT_TRUE(dnsserv.hasAnswer());
|
||||||
|
headerCheck(*parse_message, default_qid, Rcode::REFUSED(),
|
||||||
|
Opcode::QUERY().getCode(), QR_FLAG, 1, 0, 0, 0);
|
||||||
|
|
||||||
|
// Same query, but with an explicit "DROP" ACL entry. There should be
|
||||||
|
// no response.
|
||||||
|
parse_message->clear(Message::PARSE);
|
||||||
|
response_message->clear(Message::RENDER);
|
||||||
|
response_obuffer->clear();
|
||||||
|
server.updateConfig(Element::fromJSON("{ \"query_acl\": "
|
||||||
|
" [ {\"action\": \"DROP\","
|
||||||
|
" \"from\": \"" +
|
||||||
|
string(DEFAULT_REMOTE_ADDRESS) +
|
||||||
|
"\"} ] }"));
|
||||||
|
server.processMessage(*io_message, parse_message, response_message,
|
||||||
|
response_obuffer, &dnsserv);
|
||||||
|
EXPECT_FALSE(dnsserv.hasAnswer());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,8 @@ main(int argc, char* argv[]) {
|
|||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
|
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
|
||||||
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
|
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
|
||||||
isc::log::initLogger();
|
isc::log::initLogger("resolver_test", isc::log::DEBUG,
|
||||||
|
isc::log::MAX_DEBUG_LEVEL);
|
||||||
|
|
||||||
return (isc::util::unittests::run_all());
|
return (isc::util::unittests::run_all());
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
SUBDIRS = exceptions util log cryptolink dns cc config python xfr \
|
SUBDIRS = exceptions util log cryptolink dns cc config python xfr \
|
||||||
bench asiolink asiodns nsas cache resolve testutils datasrc \
|
bench asiolink asiodns nsas cache resolve testutils datasrc \
|
||||||
server_common acl
|
acl server_common
|
||||||
|
Reference in New Issue
Block a user