From 92e07f5fe6bf8aff9793ae2a308feb6c199e142e Mon Sep 17 00:00:00 2001 From: ppom <> Date: Thu, 5 Oct 2023 12:00:00 +0200 Subject: [PATCH] ip46tables wrote `ip46tables` C minimal program to handle both ipv4 and ipv6 at the same time. fix #22 --- .gitignore | 1 + Makefile | 9 ++++ README.md | 21 ++++++--- app/reaction.yml | 6 ++- config/reaction.jsonnet | 6 ++- config/reaction.service | 16 ++++--- config/reaction.test.yml | 25 ----------- ip46tables.d/ip46tables.c | 91 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 133 insertions(+), 42 deletions(-) create mode 100644 Makefile delete mode 100644 config/reaction.test.yml create mode 100644 ip46tables.d/ip46tables.c diff --git a/.gitignore b/.gitignore index e0cc912..2e5a81f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /reaction +/ip46tables /reaction*.db /reaction*.sock /result diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6725fc2 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +all: reaction ip46tables + +clean: + rm -f reaction ip46tables +ip46tables: ip46tables.d/ip46tables.c + gcc ip46tables.d/ip46tables.c -o ip46tables + +reaction: app/* reaction.go go.mod go.sum + go build . diff --git a/README.md b/README.md index b77d08b..a276d55 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,6 @@ definitions: patterns: ip: '(([0-9]{1,3}\.){3}[0-9]{1,3})|([0-9a-fA-F:]{2,90})' - ignore: - - '127.0.0.1' - - '::1' streams: ssh: @@ -63,7 +60,6 @@ local iptablesunban = ['iptables', '-w', '-D', 'reaction', '1', '-s', '', '- patterns: { ip: { regex: @'(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:[0-9a-fA-F:]{2,90})', - ignore: ['127.0.0.1', '::1'], }, }, streams: { @@ -91,7 +87,7 @@ local iptablesunban = ['iptables', '-w', '-D', 'reaction', '1', '-s', '', '- } ``` -note that both yaml and jsonnet are extensions of json, so it is also inherently supported. +note that both yaml and jsonnet are extensions of json, so json is also inherently supported. `/etc/systemd/system/reaction.service` ```systemd @@ -124,11 +120,24 @@ if you don't know where to start it, `/var/lib/reaction` should be a sane choice the socket allowing communication between the cli and server will be created at `/run/reaction/reaction.socket`. +### `ip46tables` + +`ip46tables` is a minimal c program present in its own subdirectory with only standard posix dependencies. + +it permits to configure `iptables` and `ip6tables` at the same time. +it will execute `iptables` when detecting ipv4, `ip6tables` when detecting ipv6 and both if no ip address is present on the command line. + ### compilation -you'll need the go toolchain. +you'll need the go toolchain for reaction and a c compiler for ip46tables. +```shell +$ make +``` + +alternatively, ```shell $ go build . +$ gcc ip46tables.d/ip46tables.c -o ip46tables ``` ### nixos diff --git a/app/reaction.yml b/app/reaction.yml index 9f20cec..6629969 100644 --- a/app/reaction.yml +++ b/app/reaction.yml @@ -3,8 +3,10 @@ # using YAML anchors `&name` and pointers `*name` # definitions are not readed by reaction definitions: - - &iptablesban [ "iptables" "-w" "-A" "reaction" "1" "-s" "" "-j" "DROP" ] - - &iptablesunban [ "iptables" "-w" "-D" "reaction" "1" "-s" "" "-j" "DROP" ] + - &iptablesban [ "ip46tables" "-w" "-A" "reaction" "1" "-s" "" "-j" "DROP" ] + - &iptablesunban [ "ip46tables" "-w" "-D" "reaction" "1" "-s" "" "-j" "DROP" ] +# ip46tables is a minimal C program (only POSIX dependencies) present as a subdirectory. +# it permits to handle both ipv4/iptables and ipv6/ip6tables commands # patterns are substitued in regexes. # when a filter performs an action, it replaces the found pattern diff --git a/config/reaction.jsonnet b/config/reaction.jsonnet index 252d3b3..027eb14 100644 --- a/config/reaction.jsonnet +++ b/config/reaction.jsonnet @@ -3,8 +3,10 @@ // JSONNET is a superset of JSON, so one can write plain JSON files if wanted. // variables defined for later use. -local iptablesban = ['iptables', '-w', '-A', 'reaction', '1', '-s', '', '-j', 'DROP']; -local iptablesunban = ['iptables', '-w', '-D', 'reaction', '1', '-s', '', '-j', 'DROP']; +local iptablesban = ['ip46tables', '-w', '-A', 'reaction', '1', '-s', '', '-j', 'DROP']; +local iptablesunban = ['ip46tables', '-w', '-D', 'reaction', '1', '-s', '', '-j', 'DROP']; +// ip46tables is a minimal C program (only POSIX dependencies) present as a subdirectory. +// it permits to handle both ipv4/iptables and ipv6/ip6tables commands { // patterns are substitued in regexes. diff --git a/config/reaction.service b/config/reaction.service index 3b74f8e..0d00b54 100644 --- a/config/reaction.service +++ b/config/reaction.service @@ -7,20 +7,22 @@ WantedBy=multi-user.target ExecStart=/path/to/reaction -c /etc/reaction.yml # Create an iptables chain for reaction -ExecStartPre=/path/to/iptables -w -N reaction +ExecStartPre=/path/to/ip46tables -w -N reaction # Set its default to ACCEPT -ExecStartPre=/path/to/iptables -w -A reaction -j ACCEPT +ExecStartPre=/path/to/ip46tables -w -A reaction -j ACCEPT # Always accept 127.0.0.1 -ExecStartPre=/path/to/iptables -w -I reaction 1 -s 127.0.0.1 -j ACCEPT +ExecStartPre=/path/to/ip46tables -w -I reaction 1 -s 127.0.0.1 -j ACCEPT +# Always accept ::1 +ExecStartPre=/path/to/ip46tables -w -I reaction 1 -s ::1 -j ACCEPT # Insert this chain as the first item of the INPUT chain (for incoming connections) -ExecStartPre=/path/to/iptables -w -I INPUT -p all -j reaction +ExecStartPre=/path/to/ip46tables -w -I INPUT -p all -j reaction # Remove the chain from the INPUT chain -ExecStopPost=/path/to/iptables -w -D INPUT -p all -j reaction +ExecStopPost=/path/to/ip46tables -w -D INPUT -p all -j reaction # Empty the chain -ExecStopPost=/path/to/iptables -w -F reaction +ExecStopPost=/path/to/ip46tables -w -F reaction # Delete the chain -ExecStopPost=/path/to/iptables -w -X reaction +ExecStopPost=/path/to/ip46tables -w -X reaction # Ask systemd to create /var/lib/reaction (/var/lib/ is implicit) StateDirectory=reaction diff --git a/config/reaction.test.yml b/config/reaction.test.yml deleted file mode 100644 index b5cf9f0..0000000 --- a/config/reaction.test.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- -patterns: - num: - regex: '[0-9]+' - ip: - regex: '(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:[0-9a-fA-F:]{2,90})' - ignore: - - 1.0.0.1 - -streams: - tailDown1: - cmd: [ "sh", "-c", "echo 1 2 3 4 5 1 2 3 4 5 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 | tr ' ' '\n' | while read i; do sleep 2; echo found $(($i % 10)); done" ] - filters: - findIP: - regex: - - '^found $' - retry: 3 - retryperiod: 30s - actions: - damn: - cmd: [ "echo", "" ] - undamn: - cmd: [ "echo", "undamn", "" ] - after: 30s - onexit: true diff --git a/ip46tables.d/ip46tables.c b/ip46tables.d/ip46tables.c new file mode 100644 index 0000000..3af3c78 --- /dev/null +++ b/ip46tables.d/ip46tables.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include + +// If this programs +// - receives an ipv4 address in its arguments: +// → it will executes iptables with the same arguments in place. +// +// - receives an ipv6 address in its arguments: +// → it will executes ip6tables with the same arguments in place. +// +// - doesn't receive an ipv4 or ipv6 address in its arguments: +// → it will executes both, with the same arguments in place. + +int isIPv4(char *tab) { + int i,len; + // IPv4 addresses are at least 7 chars long + len = strlen(tab); + if (len < 7 || !isdigit(tab[0]) || !isdigit(tab[len-1])) { + return 0; + } + // Each char must be a digit or a dot between 2 digits + for (i=1; i= 'a' && tab[i] <= 'f') && !(tab[i] >= 'A' && tab[i] <= 'F')) { + return 0; + } + } + return 1; +} + +int guess_type(int len, char *tab[]) { + int i; + for (i=0; i