From 221040ee1c197fde7460bf16723058e0d09d707a Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 30 May 2022 23:46:48 +0200 Subject: [PATCH] ruleset: emit time ranges when both start and stop times are specified Instead of emitting two time matches checking the start and stop time/hour respectively, emit a range when both are given. This also properly deals with time of day ranges where start > stop since nft will take care of implicitly inverting those. Also add basic test coverage for time related matches. Fixes: #9923 Signed-off-by: Jo-Philipp Wich --- .../usr/share/firewall4/templates/redirect.uc | 26 +- root/usr/share/firewall4/templates/rule.uc | 26 +- tests/03_rules/09_time | 232 ++++++++++++++++++ 3 files changed, 260 insertions(+), 24 deletions(-) create mode 100644 tests/03_rules/09_time diff --git a/root/usr/share/firewall4/templates/redirect.uc b/root/usr/share/firewall4/templates/redirect.uc index 3d1c113..5b81f64 100644 --- a/root/usr/share/firewall4/templates/redirect.uc +++ b/root/usr/share/firewall4/templates/redirect.uc @@ -35,18 +35,20 @@ {%+ if (redirect.limit): -%} limit rate {{ redirect.limit.rate }}/{{ redirect.limit.unit }} {%- if (redirect.limit_burst): %} burst {{ redirect.limit_burst }} packets{% endif %} {%+ endif -%} -{%+ if (redirect.start_date): -%} - meta time >= {{ - exists(redirect.start_date, "hour") ? fw4.datetime(redirect.start_date) : fw4.date(redirect.start_date) - }} {%+ endif -%} -{%+ if (redirect.stop_date): -%} - meta time <= {{ - exists(redirect.stop_date, "hour") ? fw4.datetime(redirect.stop_date) : fw4.date(redirect.stop_date) - }} {%+ endif -%} -{%+ if (redirect.start_time): -%} - meta hour >= {{ fw4.time(redirect.start_time) }} {%+ endif -%} -{%+ if (redirect.stop_time): -%} - meta hour <= {{ fw4.time(redirect.stop_time) }} {%+ endif -%} +{%+ if (redirect.start_date && redirect.stop_date): -%} + meta time {{ fw4.datestamp(redirect.start_date) }}-{{ fw4.datestamp(redirect.stop_date) }} {%+ + elif (redirect.start_date): -%} + meta time >= {{ fw4.datestamp(redirect.start_date) }} {%+ + elif (redirect.stop_date): -%} + meta time <= {{ fw4.datestamp(redirect.stop_date) }} {%+ + endif -%} +{%+ if (redirect.start_time && redirect.stop_time): -%} + meta hour {{ fw4.time(redirect.start_time) }}-{{ fw4.time(redirect.stop_time) }} {%+ + elif (redirect.start_time): -%} + meta hour >= {{ fw4.time(redirect.start_time) }} {%+ + elif (redirect.stop_time): -%} + meta hour <= {{ fw4.time(redirect.stop_time) }} {%+ + endif -%} {%+ if (redirect.weekdays): -%} meta day{% if (redirect.weekdays.invert): %} !={% endif %} {{ fw4.set(redirect.weekdays.days) }} {%+ endif -%} {%+ if (redirect.mark && redirect.mark.mask < 0xFFFFFFFF): -%} diff --git a/root/usr/share/firewall4/templates/rule.uc b/root/usr/share/firewall4/templates/rule.uc index 9b169ef..d2c31b1 100644 --- a/root/usr/share/firewall4/templates/rule.uc +++ b/root/usr/share/firewall4/templates/rule.uc @@ -41,18 +41,20 @@ {%+ if (rule.limit): -%} limit rate {{ rule.limit.rate }}/{{ rule.limit.unit }} {%- if (rule.limit_burst): %} burst {{ rule.limit_burst }} packets{% endif %} {%+ endif -%} -{%+ if (rule.start_date): -%} - meta time >= {{ - exists(rule.start_date, "hour") ? fw4.datetime(rule.start_date) : fw4.date(rule.start_date) - }} {%+ endif -%} -{%+ if (rule.stop_date): -%} - meta time <= {{ - exists(rule.stop_date, "hour") ? fw4.datetime(rule.stop_date) : fw4.date(rule.stop_date) - }} {%+ endif -%} -{%+ if (rule.start_time): -%} - meta hour >= {{ fw4.time(rule.start_time) }} {%+ endif -%} -{%+ if (rule.stop_time): -%} - meta hour <= {{ fw4.time(rule.stop_time) }} {%+ endif -%} +{%+ if (rule.start_date && rule.stop_date): -%} + meta time {{ fw4.datestamp(rule.start_date) }}-{{ fw4.datestamp(rule.stop_date) }} {%+ + elif (rule.start_date): -%} + meta time >= {{ fw4.datestamp(rule.start_date) }} {%+ + elif (rule.stop_date): -%} + meta time <= {{ fw4.datestamp(rule.stop_date) }} {%+ + endif -%} +{%+ if (rule.start_time && rule.stop_time): -%} + meta hour {{ fw4.time(rule.start_time) }}-{{ fw4.time(rule.stop_time) }} {%+ + elif (rule.start_time): -%} + meta hour >= {{ fw4.time(rule.start_time) }} {%+ + elif (rule.stop_time): -%} + meta hour <= {{ fw4.time(rule.stop_time) }} {%+ + endif -%} {%+ if (rule.weekdays): -%} meta day{% if (rule.weekdays.invert): %} !={% endif %} {{ fw4.set(rule.weekdays.days) }} {%+ endif -%} {%+ if (rule.mark && rule.mark.mask < 0xFFFFFFFF): -%} diff --git a/tests/03_rules/09_time b/tests/03_rules/09_time new file mode 100644 index 0000000..c3610be --- /dev/null +++ b/tests/03_rules/09_time @@ -0,0 +1,232 @@ +Ensure that time constraints are properly rendered. + +-- Testcase -- +{% + include("./root/usr/share/firewall4/main.uc", { + getenv: function(varname) { + switch (varname) { + case 'ACTION': + return 'print'; + } + } + }) +%} +-- End -- + +-- File uci/helpers.json -- +{} +-- End -- + +-- File uci/firewall.json -- +{ + "rule": [ + { + ".description": "Check parsing a complete ISO datetime stamp", + "name": "Time rule #1", + "proto": "all", + "start_date": "2022-05-30T21:51:23", + "target": "ACCEPT" + }, + { + ".description": "Check parsing a datetime stamp without seconds", + "name": "Time rule #2", + "proto": "all", + "start_date": "2022-05-30T21:51", + "target": "ACCEPT" + }, + { + ".description": "Check parsing a datetime stamp without minutes and seconds", + "name": "Time rule #3", + "proto": "all", + "start_date": "2022-05-30T21", + "target": "ACCEPT" + }, + { + ".description": "Check parsing a datetime stamp without time", + "name": "Time rule #4", + "proto": "all", + "start_date": "2022-05-30", + "target": "ACCEPT" + }, + { + ".description": "Check parsing a datetime stamp without day and time", + "name": "Time rule #5", + "proto": "all", + "start_date": "2022-05", + "target": "ACCEPT" + }, + { + ".description": "Check parsing a datetime stamp without month, day and time", + "name": "Time rule #6", + "proto": "all", + "start_date": "2022", + "target": "ACCEPT" + }, + + { + ".description": "Check parsing a complete timestamp", + "name": "Time rule #7", + "proto": "all", + "start_time": "21:51:23", + "target": "ACCEPT" + }, + { + ".description": "Check parsing a timestamp without seconds", + "name": "Time rule #8", + "proto": "all", + "start_time": "21:51", + "target": "ACCEPT" + }, + { + ".description": "Check parsing a timestamp without minutes and seconds", + "name": "Time rule #9", + "proto": "all", + "start_time": "21", + "target": "ACCEPT" + }, + + { + ".description": "Check emitting datetime ranges", + "name": "Time rule #10", + "proto": "all", + "start_date": "2022-05-30T21:51:23", + "stop_date": "2022-06-01T23:51:23", + "target": "ACCEPT" + }, + { + ".description": "Check emitting time ranges", + "name": "Time rule #11", + "proto": "all", + "start_time": "21:51:23", + "stop_time": "23:51:23", + "target": "ACCEPT" + }, + + { + ".description": "Check parsing weekdays", + "name": "Time rule #12", + "proto": "all", + "weekdays": "Monday tuEsday wed SUN Th", + "target": "ACCEPT" + }, + ] +} +-- End -- + +-- Expect stdout -- +table inet fw4 +flush table inet fw4 + +table inet fw4 { + # + # Set definitions + # + + + # + # Defines + # + + + # + # User includes + # + + include "/etc/nftables.d/*.nft" + + + # + # Filter rules + # + + chain input { + type filter hook input priority filter; policy drop; + + iifname "lo" accept comment "!fw4: Accept traffic from loopback" + + ct state established,related accept comment "!fw4: Allow inbound established and related flows" + } + + chain forward { + type filter hook forward priority filter; policy drop; + + ct state established,related accept comment "!fw4: Allow forwarded established and related flows" + } + + chain output { + type filter hook output priority filter; policy drop; + + oifname "lo" accept comment "!fw4: Accept traffic towards loopback" + + ct state established,related accept comment "!fw4: Allow outbound established and related flows" + meta time >= "2022-05-30 21:51:23" counter accept comment "!fw4: Time rule #1" + meta time >= "2022-05-30 21:51:00" counter accept comment "!fw4: Time rule #2" + meta time >= "2022-05-30 21:00:00" counter accept comment "!fw4: Time rule #3" + meta time >= "2022-05-30 00:00:00" counter accept comment "!fw4: Time rule #4" + meta time >= "2022-05-01 00:00:00" counter accept comment "!fw4: Time rule #5" + meta time >= "2022-01-01 00:00:00" counter accept comment "!fw4: Time rule #6" + meta hour >= "21:51:23" counter accept comment "!fw4: Time rule #7" + meta hour >= "21:51:00" counter accept comment "!fw4: Time rule #8" + meta hour >= "21:00:00" counter accept comment "!fw4: Time rule #9" + meta time "2022-05-30 21:51:23"-"2022-06-01 23:51:23" counter accept comment "!fw4: Time rule #10" + meta hour "21:51:23"-"23:51:23" counter accept comment "!fw4: Time rule #11" + meta day { "Monday", "Tuesday", "Wednesday", "Sunday", "Thursday" } counter accept comment "!fw4: Time rule #12" + } + + chain handle_reject { + meta l4proto tcp reject with tcp reset comment "!fw4: Reject TCP traffic" + reject with icmpx type port-unreachable comment "!fw4: Reject any other traffic" + } + + + # + # NAT rules + # + + chain dstnat { + type nat hook prerouting priority dstnat; policy accept; + } + + chain srcnat { + type nat hook postrouting priority srcnat; policy accept; + } + + + # + # Raw rules (notrack & helper) + # + + chain raw_prerouting { + type filter hook prerouting priority raw; policy accept; + } + + chain raw_output { + type filter hook output priority raw; policy accept; + } + + + # + # Mangle rules + # + + chain mangle_prerouting { + type filter hook prerouting priority mangle; policy accept; + } + + chain mangle_postrouting { + type filter hook postrouting priority mangle; policy accept; + } + + chain mangle_input { + type filter hook input priority mangle; policy accept; + } + + chain mangle_output { + type route hook output priority mangle; policy accept; + } + + chain mangle_forward { + type filter hook forward priority mangle; policy accept; + } +} +-- End -- -- 2.30.2