From 9f62bc01e5be5a63f517ec7ff749ab8e23975b77 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ale=C5=A1=20Mr=C3=A1zek?= Date: Fri, 31 Mar 2023 15:05:15 +0200 Subject: [PATCH] manager: datamodel: template: view macros --- .../datamodel/templates/config.lua.j2 | 8 +-- .../templates/macros/view_macros.lua.j2 | 29 ++++++++-- .../datamodel/templates/views.lua.j2 | 21 ++++++- .../datamodel/view_schema.py | 10 +++- .../datamodel/templates/test_view_macros.py | 57 ++++++++++++++----- 5 files changed, 97 insertions(+), 28 deletions(-) diff --git a/manager/knot_resolver_manager/datamodel/templates/config.lua.j2 b/manager/knot_resolver_manager/datamodel/templates/config.lua.j2 index aa700d06e..3b51bea77 100644 --- a/manager/knot_resolver_manager/datamodel/templates/config.lua.j2 +++ b/manager/knot_resolver_manager/datamodel/templates/config.lua.j2 @@ -22,19 +22,19 @@ nsid.name('{{ cfg.nsid }}_' .. worker.id) {% include "network.lua.j2" %} -- VIEWS section ------------------------------------ -{% include "views.lua.j2" %} +{# {% include "views.lua.j2" %} #} -- LOCAL-DATA section ----------------------------- {% include "local_data.lua.j2" %} -- SLICES section ----------------------------------- --- {% include "slices.lua.j2" %} +{# {% include "slices.lua.j2" %} #} -- POLICY section ----------------------------------- --- {% include "policy.lua.j2" %} +{# {% include "policy.lua.j2" %} #} -- RPZ section -------------------------------------- --- {% include "rpz.lua.j2" %} +{# {% include "rpz.lua.j2" %} #} -- FORWARD section ---------------------------- {% include "forward.lua.j2" %} diff --git a/manager/knot_resolver_manager/datamodel/templates/macros/view_macros.lua.j2 b/manager/knot_resolver_manager/datamodel/templates/macros/view_macros.lua.j2 index b829f2efb..160a8247c 100644 --- a/manager/knot_resolver_manager/datamodel/templates/macros/view_macros.lua.j2 +++ b/manager/knot_resolver_manager/datamodel/templates/macros/view_macros.lua.j2 @@ -1,7 +1,26 @@ -{% macro view_tsig(tsig, rule) -%} -view:tsig('{{ tsig }}',{{ rule }}) +{% macro view_insert_action(subnet, action) -%} +assert(C.kr_view_insert_action('{{ subnet }}',{{ action }})==0) {%- endmacro %} -{% macro view_addr(addr, rule) -%} -view:addr('{{ addr }}',{{ rule }}) -{%- endmacro %} \ No newline at end of file + +{% macro view_options_flags(options) -%} +policy.FLAGS({ +{%- if not options.minimize -%} +'NO_MINIMIZE', +{%- endif -%} +{%- if not options.dns64 -%} +'DNS64_DISABLE', +{%- endif -%} +}) +{%- endmacro %} + + +{% macro view_answer(answer) -%} +{%- if answer == 'allow' -%} +policy.TAGS_ASSIGN({}) +{%- elif answer == 'refused' -%} +policy.REFUSE +{%- elif answer == 'noanswer' -%} +policy.NO_ANSWER +{%- endif -%} +{%- endmacro %} diff --git a/manager/knot_resolver_manager/datamodel/templates/views.lua.j2 b/manager/knot_resolver_manager/datamodel/templates/views.lua.j2 index fbe8617e4..d9a13241b 100644 --- a/manager/knot_resolver_manager/datamodel/templates/views.lua.j2 +++ b/manager/knot_resolver_manager/datamodel/templates/views.lua.j2 @@ -1,3 +1,18 @@ -{% if cfg.views %} -modules.load('view') -{% endif %} \ No newline at end of file +{% from 'macros/view_macros.lua.j2' import view_insert_action, view_options_flags, view_answer %} +{% from 'macros/policy_macros.lua.j2' import policy_tags_assign %} + +local C = require('ffi').C + +{% for view in cfg.views %} +{% for subnet in view.subnets %} + +{% if view.tags -%} +{{ view_insert_action(subnet, policy_tags_assign(view.tags)) }} +{% elif view.answer -%} +{{ view_insert_action(subnet, view_answer(view.answer)) }} +{%- endif %} + +{{ view_insert_action(subnet, view_options_flags(view.options)) }} + +{% endfor %} +{% endfor %} diff --git a/manager/knot_resolver_manager/datamodel/view_schema.py b/manager/knot_resolver_manager/datamodel/view_schema.py index 7609651e0..2ed8fa1d7 100644 --- a/manager/knot_resolver_manager/datamodel/view_schema.py +++ b/manager/knot_resolver_manager/datamodel/view_schema.py @@ -24,11 +24,15 @@ class ViewSchema(ConfigSchema): --- subnets: Identifies the client based on his subnet. tags: Tags to link with other policy rules. - options: Configuration options for clients identified by the view. answer: Direct approach how to handle request from clients identified by the view. + options: Configuration options for clients identified by the view. """ - subnets: Optional[Union[List[IPNetwork], IPNetwork]] = None + subnets: Optional[Union[List[IPNetwork], IPNetwork]] tags: Optional[List[IDPattern]] = None + answer: Optional[Literal["allow", "refused", "noanswer"]] = None options: ViewOptionsSchema = ViewOptionsSchema() - answer: Optional[Literal["allow", "refused"]] = None + + def _validate(self) -> None: + if bool(self.tags) == bool(self.answer): + raise ValueError("only one of 'tags' and 'answer' options must be configured") diff --git a/manager/tests/unit/datamodel/templates/test_view_macros.py b/manager/tests/unit/datamodel/templates/test_view_macros.py index 32d881e23..f496784ee 100644 --- a/manager/tests/unit/datamodel/templates/test_view_macros.py +++ b/manager/tests/unit/datamodel/templates/test_view_macros.py @@ -1,22 +1,53 @@ +from typing import Any + +import pytest + from knot_resolver_manager.datamodel.config_schema import template_from_str -from knot_resolver_manager.datamodel.types import IPAddressOptionalPort +from knot_resolver_manager.datamodel.view_schema import ViewOptionsSchema, ViewSchema + + +def test_view_insert_action(): + subnet = "10.0.0.0/8" + action = "policy.DENY" + tmpl_str = """{% from 'macros/view_macros.lua.j2' import view_insert_action %} +{{ view_insert_action(subnet, action) }}""" + + tmpl = template_from_str(tmpl_str) + assert tmpl.render(subnet=subnet, action=action) == f"assert(C.kr_view_insert_action('{ subnet }',{ action })==0)" + + +def test_view_options_flags(): + tmpl_str = """{% from 'macros/view_macros.lua.j2' import view_options_flags %} +{{ view_options_flags(options) }}""" + + tmpl = template_from_str(tmpl_str) + options = ViewOptionsSchema({"dns64": False, "minimize": False}) + assert tmpl.render(options=options) == "policy.FLAGS({'NO_MINIMIZE','DNS64_DISABLE',})" + assert tmpl.render(options=ViewOptionsSchema()) == "policy.FLAGS({})" -def test_view_tsig(): - tsig: str = r"\5mykey" - rule = "policy.all(policy.DENY)" - tmpl_str = """{% from 'macros/view_macros.lua.j2' import view_tsig %} -{{ view_tsig(tsig, rule) }}""" +def test_view_answer(): + tmpl_str = """{% from 'macros/view_macros.lua.j2' import view_options_flags %} +{{ view_options_flags(options) }}""" tmpl = template_from_str(tmpl_str) - assert tmpl.render(tsig=tsig, rule=rule) == f"view:tsig('{tsig}',{rule})" + options = ViewOptionsSchema({"dns64": False, "minimize": False}) + assert tmpl.render(options=options) == "policy.FLAGS({'NO_MINIMIZE','DNS64_DISABLE',})" + assert tmpl.render(options=ViewOptionsSchema()) == "policy.FLAGS({})" -def test_view_addr(): - addr: IPAddressOptionalPort = IPAddressOptionalPort("10.0.0.1") - rule = "policy.all(policy.DENY)" - tmpl_str = """{% from 'macros/view_macros.lua.j2' import view_addr %} -{{ view_addr(addr, rule) }}""" +@pytest.mark.parametrize( + "val,res", + [ + ("allow", "policy.TAGS_ASSIGN({})"), + ("refused", "policy.REFUSE"), + ("noanswer", "policy.NO_ANSWER"), + ], +) +def test_view_answer(val: Any, res: Any): + tmpl_str = """{% from 'macros/view_macros.lua.j2' import view_answer %} +{{ view_answer(view.answer) }}""" tmpl = template_from_str(tmpl_str) - assert tmpl.render(addr=addr, rule=rule) == f"view:addr('{addr}',{rule})" + view = ViewSchema({"subnets": "10.0.0.0/8", "answer": val}) + assert tmpl.render(view=view) == res -- 2.47.2