rm -f ns*/named.run
rm -f ns*/rpz*.txt
rm -rf __pycache__
+rm -f ns3/*-rpz-external.local.db
+++ /dev/null
-/*
- * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
- *
- * SPDX-License-Identifier: MPL-2.0
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, you can obtain one at https://mozilla.org/MPL/2.0/.
- *
- * See the COPYRIGHT file distributed with this work for additional
- * information regarding copyright ownership.
- */
-
-key rndc_key {
- secret "1234abcd8765";
- algorithm @DEFAULT_HMAC@;
-};
-
-controls {
- inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
-};
-
-options {
- query-source address 10.53.0.1;
- notify-source 10.53.0.1;
- transfer-source 10.53.0.1;
- port @PORT@;
- listen-on { 10.53.0.1; };
- pid-file "named.pid";
- notify no;
- dnssec-validation no;
- allow-query { any; };
- recursion yes;
- allow-recursion { any; };
-
- response-policy {
- zone "rpz.local";
- };
-};
-
-logging {
- channel rpz_passthru {
- file "rpz_passthru.txt" versions 3 size 5m;
- print-time yes;
- print-category yes;
- print-severity yes;
- severity info;
- };
-
- channel rpz_log {
- file "rpz.txt" versions 3 size 20m;
- print-time yes;
- print-category yes;
- print-severity yes;
- severity info;
- };
-
- category rpz { rpz_log; default_debug; };
- category rpz-passthru { rpz_passthru; default_debug; };
-};
-
-zone "rpz.local" {
- type primary;
- file "rpz.local.db";
- allow-transfer { none; };
- allow-query { localhost; };
-};
-
-zone "." {
- type hint;
- file "root.db";
-};
-
-
--- /dev/null
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; SPDX-License-Identifier: MPL-2.0
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, you can obtain one at https://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 300
+
+@ IN SOA ns1 root.gooddomain. (
+ 2020040101
+ 4h
+ 1h
+ 1w
+ 60
+)
+
+ IN NS ns1
+
+ns1 IN A 10.53.0.2
+
+gooddomain. IN A 10.53.0.2
+www IN A 10.53.0.3
allow-transfer { none; };
};
+zone "gooddomain" {
+ type primary;
+ file "gooddomain.db";
+ allow-transfer { none; };
+};
+
+zone "rpz-external.local" {
+ type primary;
+ file "rpz-external.local.db";
+ allow-transfer { any; };
+};
--- /dev/null
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; SPDX-License-Identifier: MPL-2.0
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, you can obtain one at https://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 300
+
+@ IN SOA localhost.rpz-external.local root.rpz-external.local. (
+ 2020022500 ; serial number
+ 60 ; refresh every minute
+ 60 ; retry every minute
+ 432000 ; expire in 5 days
+ 60 ; negative caching ttl, 1 minute
+)
+
+
+ IN NS LOCALHOST.
+
+allowed IN CNAME .
+*.allowed IN CNAME .
--- /dev/null
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; SPDX-License-Identifier: MPL-2.0
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, you can obtain one at https://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 300
+
+@ IN SOA localhost.rpz.local root.rpz.local. (
+ 2020022500 ; serial number
+ 60 ; refresh every minute
+ 60 ; retry every minute
+ 432000 ; expire in 5 days
+ 60 ; negative caching ttl, 1 minute
+)
+
+
+ IN NS LOCALHOST.
+
+allowed IN CNAME rpz-passthru.
+*.allowed IN CNAME rpz-passthru.
+
+gooddomain IN CNAME .
+*.gooddomain IN CNAME .
--- /dev/null
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; SPDX-License-Identifier: MPL-2.0
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, you can obtain one at https://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 300
+
+@ IN SOA localhost.rpz-extra.local root.rpz-extra.local. (
+ 2020022500 ; serial number
+ 60 ; refresh every minute
+ 60 ; retry every minute
+ 432000 ; expire in 5 days
+ 60 ; negative caching ttl, 1 minute
+)
+
+
+ IN NS LOCALHOST.
+
+allowed IN CNAME rpz-passthru.
+*.allowed IN CNAME rpz-passthru.
+
+gooddomain IN CNAME .
+*.gooddomain IN CNAME .
+
+baddomain IN CNAME .
+*.baddomain IN CNAME .
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+key rndc_key {
+ secret "1234abcd8765";
+ algorithm @DEFAULT_HMAC@;
+};
+
+controls {
+ inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
+};
+
+options {
+ query-source address 10.53.0.3;
+ notify-source 10.53.0.3;
+ transfer-source 10.53.0.3;
+ port @PORT@;
+ listen-on { 10.53.0.3; };
+ pid-file "named.pid";
+ notify no;
+ dnssec-validation no;
+ allow-query { any; };
+ recursion yes;
+ allow-recursion { any; };
+ empty-zones-enable false;
+ response-policy {
+ zone "rpz-extra.local";
+ };
+};
+
+logging {
+ channel rpz_passthru {
+ file "rpz_passthru.txt" versions 3 size 5m;
+ print-time yes;
+ print-category yes;
+ print-severity yes;
+ severity info;
+ };
+
+ channel rpz_log {
+ file "rpz.txt" versions 3 size 20m;
+ print-time yes;
+ print-category yes;
+ print-severity yes;
+ severity info;
+ };
+
+ category rpz { rpz_log; default_debug; };
+ category rpz-passthru { rpz_passthru; default_debug; };
+};
+
+view "first" {
+ match-clients { 10.53.0.1; };
+ zone "." {
+ type hint;
+ file "root.db";
+ };
+ zone "rpz.local" {
+ type primary;
+ file "first-rpz.local.db";
+ allow-transfer { none; };
+ allow-query { localhost; };
+ };
+ response-policy {
+ zone "rpz.local";
+ };
+};
+
+view "second" {
+ match-clients { 10.53.0.2; };
+ zone "." {
+ type hint;
+ file "root.db";
+ };
+ zone "rpz-external.local" {
+ type secondary;
+ primaries { 10.53.0.2; };
+ file "second-rpz-external.local.db";
+ allow-query { 10.53.0.2; };
+ };
+ response-policy {
+ zone "rpz-external.local";
+ };
+};
+
+view "third" {
+ match-clients { 10.53.0.3; };
+ zone "." {
+ type hint;
+ file "root.db";
+ };
+ zone "rpz-extra.local" {
+ type primary;
+ file "third-rpz-extra.local.db";
+ allow-transfer { none; };
+ allow-query { localhost; };
+ };
+};
+
+view "fourth" {
+ match-clients { 10.53.0.4; };
+ zone "." {
+ type hint;
+ file "root.db";
+ };
+ zone "rpz-extra.local" {
+ type primary;
+ file "fourth-rpz-extra.local.db";
+ allow-transfer { none; };
+ allow-query { localhost; };
+ };
+};
+
+view "external" {
+ match-clients { any; };
+ zone "." {
+ type hint;
+ file "root.db";
+ };
+ zone "rpz.local" {
+ type primary;
+ file "external-rpz.local.db";
+ allow-transfer { none; };
+ allow-query { localhost; };
+ };
+ zone "rpz-external.local" {
+ type secondary;
+ masterfile-format text;
+ primaries { 10.53.0.2; };
+ file "external-rpz-external.local.db";
+ allow-query { 10.53.0.5; };
+ };
+ response-policy {
+ zone "rpz-external.local";
+ zone "rpz.local";
+ };
+};
baddomain. NS ns1.baddomain.
ns1.baddomain. A 10.53.0.2
+
+gooddomain. NS ns1.gooddomain.
+ns1.gooddomain. A 10.53.0.2
--- /dev/null
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; SPDX-License-Identifier: MPL-2.0
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, you can obtain one at https://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 300
+
+@ IN SOA localhost.rpz-extra.local root.rpz-extra.local. (
+ 2020022500 ; serial number
+ 60 ; refresh every minute
+ 60 ; retry every minute
+ 432000 ; expire in 5 days
+ 60 ; negative caching ttl, 1 minute
+)
+
+
+ IN NS LOCALHOST.
+
+allowed IN CNAME rpz-passthru.
+*.allowed IN CNAME rpz-passthru.
. ../conf.sh
-copy_setports ns1/named.conf.in ns1/named.conf
copy_setports ns2/named.conf.in ns2/named.conf
+copy_setports ns3/named.conf.in ns3/named.conf
--- /dev/null
+#!/usr/bin/python3
+
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# SPDX-License-Identifier: MPL-2.0
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at https://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+import time
+
+import pytest
+
+pytest.importorskip("dns")
+import dns.resolver
+
+
+def wait_for_transfer(ip, port, client_ip, name, rrtype):
+ resolver = dns.resolver.Resolver()
+ resolver.nameservers = [ip]
+ resolver.port = port
+
+ for _ in range(10):
+ try:
+ resolver.resolve(name, rrtype, source=client_ip)
+ except dns.resolver.NoNameservers:
+ time.sleep(1)
+ else:
+ break
+ else:
+ raise RuntimeError(
+ "zone transfer failed: "
+ f"client {client_ip} got NXDOMAIN for {name} {rrtype} from @{ip}:{port}")
+
+
+def test_rpz_multiple_views(named_port):
+ resolver = dns.resolver.Resolver()
+ resolver.nameservers = ["10.53.0.3"]
+ resolver.port = named_port
+
+ wait_for_transfer("10.53.0.3", named_port, "10.53.0.2", "rpz-external.local", "SOA")
+ wait_for_transfer("10.53.0.3", named_port, "10.53.0.5", "rpz-external.local", "SOA")
+
+ # For 10.53.0.1 source IP:
+ # - baddomain.com isn't allowed (CNAME .), should return NXDOMAIN
+ # - gooddomain.com is allowed
+ # - allowed. is allowed
+ with pytest.raises(dns.resolver.NXDOMAIN):
+ resolver.resolve("baddomain.", "A", source="10.53.0.1")
+
+ ans = resolver.resolve("gooddomain.", "A", source="10.53.0.1")
+ for rd in ans:
+ assert rd.address == "10.53.0.2"
+
+ ans = resolver.resolve("allowed.", "A", source="10.53.0.1")
+ assert ans[0].address == "10.53.0.2"
+
+ # For 10.53.0.2 source IP:
+ # - allowed.com isn't allowed (CNAME .), should return NXDOMAIN
+ # - baddomain.com is allowed
+ # - gooddomain.com is allowed
+ ans = resolver.resolve("baddomain.", "A", source="10.53.0.2")
+ for rd in ans:
+ assert rd.address == "10.53.0.2"
+
+ ans = resolver.resolve("gooddomain.", "A", source="10.53.0.2")
+ for rd in ans:
+ assert rd.address == "10.53.0.2"
+
+ with pytest.raises(dns.resolver.NXDOMAIN):
+ resolver.resolve("allowed.", "A", source="10.53.0.2")
+
+ # For 10.53.0.3 source IP:
+ # - gooddomain.com is allowed
+ # - baddomain.com is allowed
+ # - allowed. is allowed
+ ans = resolver.resolve("baddomain.", "A", source="10.53.0.3")
+ for rd in ans:
+ assert rd.address == "10.53.0.2"
+
+ ans = resolver.resolve("gooddomain.", "A", source="10.53.0.3")
+ for rd in ans:
+ assert rd.address == "10.53.0.2"
+
+ ans = resolver.resolve("allowed.", "A", source="10.53.0.3")
+ assert ans[0].address == "10.53.0.2"
+
+ # For 10.53.0.4 source IP:
+ # - gooddomain.com isn't allowed (CNAME .), should return NXDOMAIN
+ # - baddomain.com isn't allowed (CNAME .), should return NXDOMAIN
+ # - allowed. is allowed
+ with pytest.raises(dns.resolver.NXDOMAIN):
+ resolver.resolve("baddomain.", "A", source="10.53.0.4")
+
+ with pytest.raises(dns.resolver.NXDOMAIN):
+ resolver.resolve("gooddomain.", "A", source="10.53.0.4")
+
+ ans = resolver.resolve("allowed.", "A", source="10.53.0.4")
+ assert ans[0].address == "10.53.0.2"
+
+ # For 10.53.0.5 (any) source IP:
+ # - baddomain.com is allowed
+ # - gooddomain.com isn't allowed (CNAME .), should return NXDOMAIN
+ # - allowed.com isn't allowed (CNAME .), should return NXDOMAIN
+ ans = resolver.resolve("baddomain.", "A", source="10.53.0.5")
+ for rd in ans:
+ assert rd.address == "10.53.0.2"
+
+ with pytest.raises(dns.resolver.NXDOMAIN):
+ resolver.resolve("gooddomain.", "A", source="10.53.0.5")
+
+ with pytest.raises(dns.resolver.NXDOMAIN):
+ resolver.resolve("allowed.", "A", source="10.53.0.5")
def test_rpz_passthru_logging(named_port):
resolver = dns.resolver.Resolver()
- resolver.nameservers = ["10.53.0.1"]
+ resolver.nameservers = ["10.53.0.3"]
resolver.port = named_port
# Should generate a log entry into rpz_passthru.txt
with pytest.raises(dns.resolver.NXDOMAIN):
resolver.resolve("baddomain.", "A", source="10.53.0.1")
- rpz_passthru_logfile = os.path.join("ns1", "rpz_passthru.txt")
- rpz_logfile = os.path.join("ns1", "rpz.txt")
+ rpz_passthru_logfile = os.path.join("ns3", "rpz_passthru.txt")
+ rpz_logfile = os.path.join("ns3", "rpz.txt")
assert os.path.isfile(rpz_passthru_logfile)
assert os.path.isfile(rpz_logfile)