diff options
| author | Supremist <sergkarv@gmail.com> | 2026-07-09 18:23:11 +0300 |
|---|---|---|
| committer | Supremist <sergkarv@gmail.com> | 2026-07-09 18:23:11 +0300 |
| commit | e3012776a657e7a1d84f6c77f57a7730480597ca (patch) | |
| tree | 1b3c51de8497679e5df6e0ede36b23eecf05d18c | |
| parent | f0aaa78ab5c49d3a6c702cc58e8b4983855c836e (diff) | |
| download | netbird-e3012776a657e7a1d84f6c77f57a7730480597ca.tar.xz netbird-e3012776a657e7a1d84f6c77f57a7730480597ca.zip | |
Update netbird ips in nft sets on monitor events
| -rw-r--r-- | api/nft-event-listener.nu | 72 | ||||
| -rw-r--r-- | api/update-netbird-nftables.nu (renamed from api/netbird-nft-sets.nu) | 28 | ||||
| -rw-r--r-- | test-data/mock-nft-mon.nu | 4 | ||||
| -rw-r--r-- | test-data/nft-mon-output.json | 3 |
4 files changed, 97 insertions, 10 deletions
diff --git a/api/nft-event-listener.nu b/api/nft-event-listener.nu new file mode 100644 index 0000000..0554c6f --- /dev/null +++ b/api/nft-event-listener.nu @@ -0,0 +1,72 @@ +def "job recv pending" []: nothing -> list { + "marker" | job send (job id) # send a marker to self + mut events = [] + mut ev = job recv # marker will prevent wait here + while $ev != "marker" { + $events = $events | append $ev + $ev = job recv + } + $events +} + +# may wait up to two intervals, because event timestamps are not accounted +def create-debounce-queue [interval: duration, handler: closure] { + job spawn -d "debounce queue" { + mut event = job recv + loop { + sleep $interval + let new_events = job recv pending + if ($new_events | is-empty) { + do $handler $event + $event = job recv + } else { + $event = ($new_events | last) + } + } + } +} + +def single [] { + let $data = $in + if ($data | length) != 1 { + make error "Expected single element" + } + $data | first +} + +def parse-nft-mon-event [] { + mut rest = $in + mut result = {} + for key_name in ['action', 'type'] { + let key = $rest | items {|k, v| $k} | single + $rest = $rest | get $key + $result = $result | insert $key_name $key + } + $result | merge $rest +} + +def main [--test] { + let debounce_interval = 2sec + let debounce_queue = create-debounce-queue $debounce_interval {|ev| + print "Triggered" $ev + # nu update-netbird-nftables.nu --apply + } + try { + if $test { + nu test-data/mock-nft-mon.nu + } else { + ^nft --json monitor rules + } | lines + | each { from json | parse-nft-mon-event } + | where action == add and type == rule and table == netbird # TODO filter update evenets + | each { job send $debounce_queue } + | ignore + print "Exiting gracefully..." + sleep $debounce_interval + sleep $debounce_interval + } catch { |e| + print $e.rendered + } finally { + job kill $debounce_queue + } +}
\ No newline at end of file diff --git a/api/netbird-nft-sets.nu b/api/update-netbird-nftables.nu index 2187aa6..95b9e02 100644 --- a/api/netbird-nft-sets.nu +++ b/api/update-netbird-nftables.nu @@ -1,7 +1,3 @@ -# Usage: -# ^nft --json list sets | nu --stdin api/netbird-nft-sets.nu -# open --raw test-data/nft-sets.json | nu --stdin api/netbird-nft-sets.nu - use netbird-api.nu def mangle-name [prefix: string, name: string] { @@ -32,15 +28,27 @@ def generate-nft-commands [] { # nft add talbe inet <name> # ignore if table already exists # nft destroy table inet <name> # ignore if table already removed let set_changes = $in - for $s in $set_changes { + $set_changes | each {|s| let path = [$s.family $s.table $s.nft_name] | str join ' ' - print $"flush set ($path)" - print $"add element ($path) { ($s.new_elem | str join ', ') }" - } + [ + $"flush set ($path)", + $"add element ($path) { ($s.new_elem | str join ', ') }" + ] + } | flatten } -def main [] { - $in | from json | get nftables | skip 1 | get set +def main [--test, --apply] { + let commands = if $test { + open --raw test-data/nft-sets.json + } else { + ^nft --json list sets + } | from json | get nftables | skip 1 | get set | find-changed-sets | generate-nft-commands + | str join "\n" + if $apply { + $commands | ^nft -f - + } else { + $commands + } } diff --git a/test-data/mock-nft-mon.nu b/test-data/mock-nft-mon.nu new file mode 100644 index 0000000..9048dc3 --- /dev/null +++ b/test-data/mock-nft-mon.nu @@ -0,0 +1,4 @@ +const events = 'test-data/nft-mon-output.json' +open --raw $events | lines | each { sleep 1sec; print $in } | ignore +sleep 5sec +open --raw $events | lines | each { sleep 1sec; print $in } | ignore
\ No newline at end of file diff --git a/test-data/nft-mon-output.json b/test-data/nft-mon-output.json new file mode 100644 index 0000000..1080082 --- /dev/null +++ b/test-data/nft-mon-output.json @@ -0,0 +1,3 @@ +{"delete": {"rule": {"family": "ip", "table": "netbird", "chain": "netbird-rt-fwd", "handle": 68, "expr": [{"match": {"op": "==", "left": {"payload": {"protocol": "ip", "field": "saddr"}}, "right": "100.66.181.207"}}, {"match": {"op": "==", "left": {"payload": {"protocol": "ip", "field": "daddr"}}, "right": "172.30.0.5"}}, {"counter": {"packets": 0, "bytes": 0}}, {"accept": null}]}}} +{"add": {"rule": {"family": "ip", "table": "netbird", "chain": "netbird-rt-fwd", "handle": 69, "expr": [{"match": {"op": "==", "left": {"payload": {"protocol": "ip", "field": "saddr"}}, "right": "100.66.181.207"}}, {"match": {"op": "==", "left": {"payload": {"protocol": "ip", "field": "daddr"}}, "right": "172.30.0.5"}}, {"counter": {"packets": 0, "bytes": 0}}, {"accept": null}]}}} +{"add": {"rule": {"family": "ip", "table": "netbird", "chain": "netbird-rt-fwd", "handle": 69, "expr": [{"match": {"op": "==", "left": {"payload": {"protocol": "ip", "field": "saddr"}}, "right": "100.66.181.207"}}, {"match": {"op": "==", "left": {"payload": {"protocol": "ip", "field": "daddr"}}, "right": "172.30.0.5"}}, {"counter": {"packets": 0, "bytes": 0}}, {"accept": null}]}}}
\ No newline at end of file |
