Implement start/stop commands
fix #41 update README and configuration files accordingly
This commit is contained in:
		
							
								
								
									
										72
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								README.md
									
									
									
									
									
								
							| @ -4,7 +4,7 @@ a program that scans program outputs, such as logs, | |||||||
| for repeated patterns, such as failed login attempts, | for repeated patterns, such as failed login attempts, | ||||||
| and takes action, such as banning ips. | and takes action, such as banning ips. | ||||||
|  |  | ||||||
| (adapted from [fail2ban](http://fail2ban.org)'s presentation 😄) | (adapted from [ fail2ban ](http://fail2ban.org)'s presentation 😄) | ||||||
|  |  | ||||||
| 🚧 this program hasn't received external audit. however, it already works well on my servers 🚧 | 🚧 this program hasn't received external audit. however, it already works well on my servers 🚧 | ||||||
|  |  | ||||||
| @ -22,16 +22,27 @@ and an always-running daemon should be implemented in a fast language. | |||||||
|  |  | ||||||
| this configuration file is all that should be needed to prevent brute force attacks on an ssh server. | this configuration file is all that should be needed to prevent brute force attacks on an ssh server. | ||||||
|  |  | ||||||
| see [reaction.service](./config/reaction.service) and [reaction.yml](./app/reaction.yml) for the fully explained examples. | see [ reaction.service ](./config/reaction.service) and [reaction.yml](./app/reaction.yml) for the fully explained examples. | ||||||
|  |  | ||||||
| `/etc/reaction.yml` | `/etc/reaction.yml` | ||||||
| ```yaml | ```yaml | ||||||
| definitions: | definitions: | ||||||
|   - &iptablesban [ "iptables" "-w" "-I" "reaction" "1" "-s" "<ip>" "-j" "block" ] |   - &iptablesban [ "ip46tables" "-w" "-I" "reaction" "1" "-s" "<ip>" "-j" "block" ] | ||||||
|   - &iptablesunban [ "iptables" "-w" "-D" "reaction" "1" "-s" "<ip>" "-j" "block" ] |   - &iptablesunban [ "ip46tables" "-w" "-D" "reaction" "1" "-s" "<ip>" "-j" "block" ] | ||||||
|  |  | ||||||
| patterns: | patterns: | ||||||
|   ip: '(([0-9]{1,3}\.){3}[0-9]{1,3})|([0-9a-fA-F:]{2,90})' |   ip: '(([ 0-9 ]{1,3}\.){3}[0-9]{1,3})|([0-9a-fA-F:]{2,90})' | ||||||
|  |  | ||||||
|  | start: | ||||||
|  |   - [ "ip46tables", "-w", "-N", "reaction" ] | ||||||
|  |   - [ "ip46tables", "-w", "-A", "reaction", "-j", "ACCEPT" ] | ||||||
|  |   - [ "ip46tables", "-w", "-I", "reaction", "1", "-s", "127.0.0.1", "-j", "ACCEPT" ] | ||||||
|  |   - [ "ip46tables", "-w", "-I", "INPUT", "-p", "all", "-j", "reaction" ] | ||||||
|  |  | ||||||
|  | stop: | ||||||
|  |   - [ "ip46tables", "-w," "-D," "INPUT", "-p", "all", "-j", "reaction" ] | ||||||
|  |   - [ "ip46tables", "-w," "-F," "reaction" ] | ||||||
|  |   - [ "ip46tables", "-w," "-X," "reaction" ] | ||||||
|  |  | ||||||
| streams: | streams: | ||||||
|   ssh: |   ssh: | ||||||
| @ -54,17 +65,29 @@ jsonnet is also supported: | |||||||
|  |  | ||||||
| `/etc/reaction.jsonnet` | `/etc/reaction.jsonnet` | ||||||
| ```jsonnet | ```jsonnet | ||||||
| local iptablesban = ['iptables', '-w', '-A', 'reaction', '1', '-s', '<ip>', '-j', 'DROP']; | local iptables(args) = [ 'ip46tables', '-w' ] + args; | ||||||
| local iptablesunban = ['iptables', '-w', '-D', 'reaction', '1', '-s', '<ip>', '-j', 'DROP']; | local iptablesban(ip) = iptables([ '-A', 'reaction', '1', '-s', ip, '-j', 'DROP' ]); | ||||||
|  | local iptablesunban(ip) = iptables([ '-D', 'reaction', '1', '-s', ip, '-j', 'DROP' ]); | ||||||
| { | { | ||||||
|   patterns: { |   patterns: { | ||||||
|     ip: { |     ip: { | ||||||
|       regex: @'(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:[0-9a-fA-F:]{2,90})', |       regex: @'(?:(?:[ 0-9 ]{1,3}\.){3}[0-9]{1,3})|(?:[0-9a-fA-F:]{2,90})', | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|  |   start: { | ||||||
|  |     iptables([ "-N", "reaction" ]), | ||||||
|  |     iptables([ "-A", "reaction", "-j", "ACCEPT" ]), | ||||||
|  |     iptables([ "-I", "reaction", "1", "-s", "127.0.0.1", "-j", "ACCEPT" ]), | ||||||
|  |     iptables([ "-I", "INPUT", "-p", "all", "-j", "reaction" ]), | ||||||
|  |   }, | ||||||
|  |   stop: { | ||||||
|  |     iptables([ "-D,", "INPUT", "-p", "all", "-j", "reaction" ]), | ||||||
|  |     iptables([ "-F,", "reaction" ]), | ||||||
|  |     iptables([ "-X,", "reaction" ]), | ||||||
|  |   }, | ||||||
|   streams: { |   streams: { | ||||||
|     ssh: { |     ssh: { | ||||||
|       cmd: ['journalctl', '-fu', 'sshd.service'], |       cmd: [ 'journalctl', '-fu', 'sshd.service' ], | ||||||
|       filters: { |       filters: { | ||||||
|         failedlogin: { |         failedlogin: { | ||||||
|           regex: [ @'authentication failure;.*rhost=<ip>' ], |           regex: [ @'authentication failure;.*rhost=<ip>' ], | ||||||
| @ -72,10 +95,10 @@ local iptablesunban = ['iptables', '-w', '-D', 'reaction', '1', '-s', '<ip>', '- | |||||||
|           retryperiod: '6h', |           retryperiod: '6h', | ||||||
|           actions: { |           actions: { | ||||||
|             ban: { |             ban: { | ||||||
|               cmd: iptablesban, |               cmd: iptablesban('<ip>'), | ||||||
|             }, |             }, | ||||||
|             unban: { |             unban: { | ||||||
|               cmd: iptablesunban, |               cmd: iptablesunban('<ip>'), | ||||||
|               after: '48h', |               after: '48h', | ||||||
|               onexit: true, |               onexit: true, | ||||||
|             }, |             }, | ||||||
| @ -91,21 +114,12 @@ note that both yaml and jsonnet are extensions of json, so json is also inherent | |||||||
|  |  | ||||||
| `/etc/systemd/system/reaction.service` | `/etc/systemd/system/reaction.service` | ||||||
| ```systemd | ```systemd | ||||||
| [Unit] | [ Unit ] | ||||||
| WantedBy=multi-user.target | WantedBy=multi-user.target | ||||||
|  |  | ||||||
| [Service] | [ Service ] | ||||||
| ExecStart=/path/to/reaction -c /etc/reaction.yml | ExecStart=/path/to/reaction -c /etc/reaction.yml | ||||||
|  |  | ||||||
| ExecStartPre=/path/to/iptables -w -N reaction |  | ||||||
| ExecStartPre=/path/to/iptables -w -A reaction -j ACCEPT |  | ||||||
| ExecStartPre=/path/to/iptables -w -I reaction 1 -s 127.0.0.1 -j ACCEPT |  | ||||||
| ExecStartPre=/path/to/iptables -w -I INPUT -p all -j reaction |  | ||||||
|  |  | ||||||
| ExecStopPost=/path/to/iptables -w -D INPUT -p all -j reaction |  | ||||||
| ExecStopPost=/path/to/iptables -w -F reaction |  | ||||||
| ExecStopPost=/path/to/iptables -w -X reaction |  | ||||||
|  |  | ||||||
| StateDirectory=reaction | StateDirectory=reaction | ||||||
| RuntimeDirectory=reaction | RuntimeDirectory=reaction | ||||||
| WorkingDirectory=/var/lib/reaction | WorkingDirectory=/var/lib/reaction | ||||||
| @ -142,11 +156,11 @@ $ gcc ip46tables.d/ip46tables.c -o ip46tables | |||||||
|  |  | ||||||
| ### nixos | ### nixos | ||||||
|  |  | ||||||
| in addition to the [package](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/pkgs/reaction/default.nix) | in addition to the [ package ](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/pkgs/reaction/default.nix) | ||||||
| and [module](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/modules/common/reaction.nix) | and [ module ](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/modules/common/reaction.nix) | ||||||
| that i didn't try to upstream to nixpkgs yet (although they are ready), i use extensively reaction on my servers. if you're using nixos, | that i didn't try to upstream to nixpkgs yet (although they are ready), i use extensively reaction on my servers. if you're using nixos, | ||||||
| consider reading and building upon [my own building blocks](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/modules/common/reaction-variables.nix), | consider reading and building upon [ my own building blocks ](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/modules/common/reaction-variables.nix), | ||||||
| [my own non-root reaction conf, including conf for SSH, port scanning & Nginx common attack URLS](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/modules/common/reaction-custom.nix), | [ my own non-root reaction conf, including conf for SSH, port scanning & Nginx common attack URLS ](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/modules/common/reaction-custom.nix), | ||||||
| and the configuration for [nextcloud](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/modules/musi/file.ppom.me.nix#L53), | and the configuration for [ nextcloud ](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/modules/musi/file.ppom.me.nix#L53), | ||||||
| [vaultwarden](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/modules/musi/vaultwarden.nix#L45), | [ vaultwarden ](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/modules/musi/vaultwarden.nix#L45), | ||||||
| and [maddy](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/modules/musi/mail.nix#L74). see also an [example](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/modules/musi/mail.nix#L85) where it does something else than banning IPs. | and [ maddy ](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/modules/musi/mail.nix#L74). see also an [example](https://framagit.org/ppom/nixos/-/blob/cf5448b21ae3386265485308a6cd077e8068ad77/modules/musi/mail.nix#L85) where it does something else than banning IPs. | ||||||
|  | |||||||
| @ -39,6 +39,15 @@ func cmdStdout(commandline []string) chan *string { | |||||||
| 	return lines | 	return lines | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func runCommands(commands [][]string, moment string) { | ||||||
|  | 	for _, command := range commands { | ||||||
|  | 		cmd := exec.Command(command[0], command[1:]...) | ||||||
|  | 		if err := cmd.Start(); err != nil { | ||||||
|  | 			logger.Printf(logger.ERROR, "couldn't execute %v command: %v", moment, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (p *Pattern) notAnIgnore(match *string) bool { | func (p *Pattern) notAnIgnore(match *string) bool { | ||||||
| 	for _, ignore := range p.Ignore { | 	for _, ignore := range p.Ignore { | ||||||
| 		if ignore == *match { | 		if ignore == *match { | ||||||
| @ -323,6 +332,8 @@ func Daemon(confFilename string) { | |||||||
| 	actions = make(ActionsMap) | 	actions = make(ActionsMap) | ||||||
| 	matches = make(MatchesMap) | 	matches = make(MatchesMap) | ||||||
|  |  | ||||||
|  | 	runCommands(conf.Start, "start") | ||||||
|  |  | ||||||
| 	go DatabaseManager(conf) | 	go DatabaseManager(conf) | ||||||
| 	go MatchesManager() | 	go MatchesManager() | ||||||
| 	go ActionsManager() | 	go ActionsManager() | ||||||
| @ -348,16 +359,16 @@ func Daemon(confFilename string) { | |||||||
| 			logger.Printf(logger.ERROR, "%s stream finished", finishedStream.name) | 			logger.Printf(logger.ERROR, "%s stream finished", finishedStream.name) | ||||||
| 			nbStreamsInExecution-- | 			nbStreamsInExecution-- | ||||||
| 			if nbStreamsInExecution == 0 { | 			if nbStreamsInExecution == 0 { | ||||||
| 				quit() | 				quit(conf) | ||||||
| 			} | 			} | ||||||
| 		case <-sigs: | 		case <-sigs: | ||||||
| 			logger.Printf(logger.INFO, "Received SIGINT/SIGTERM, exiting") | 			logger.Printf(logger.INFO, "Received SIGINT/SIGTERM, exiting") | ||||||
| 			quit() | 			quit(conf) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func quit() { | func quit(conf *Conf) { | ||||||
| 	// send stop to StreamManager·s | 	// send stop to StreamManager·s | ||||||
| 	close(stopStreams) | 	close(stopStreams) | ||||||
| 	logger.Println(logger.INFO, "Waiting for Streams to finish...") | 	logger.Println(logger.INFO, "Waiting for Streams to finish...") | ||||||
| @ -369,6 +380,8 @@ func quit() { | |||||||
| 	// stop all actions | 	// stop all actions | ||||||
| 	logger.Println(logger.INFO, "Waiting for Actions to finish...") | 	logger.Println(logger.INFO, "Waiting for Actions to finish...") | ||||||
| 	wgActions.Wait() | 	wgActions.Wait() | ||||||
|  | 	// run stop commands | ||||||
|  | 	runCommands(conf.Stop, "stop") | ||||||
| 	// delete pipe | 	// delete pipe | ||||||
| 	err := os.Remove(*SocketPath) | 	err := os.Remove(*SocketPath) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  | |||||||
| @ -3,8 +3,8 @@ | |||||||
| # using YAML anchors `&name` and pointers `*name` | # using YAML anchors `&name` and pointers `*name` | ||||||
| # definitions are not readed by reaction | # definitions are not readed by reaction | ||||||
| definitions: | definitions: | ||||||
|   - &iptablesban [ "ip46tables" "-w" "-A" "reaction" "1" "-s" "<ip>" "-j" "DROP" ] |   - &iptablesban [ "ip46tables", "-w", "-A", "reaction", "1", "-s", "<ip>", "-j", "DROP" ] | ||||||
|   - &iptablesunban [ "ip46tables" "-w" "-D" "reaction" "1" "-s" "<ip>" "-j" "DROP" ] |   - &iptablesunban [ "ip46tables", "-w", "-D", "reaction", "1", "-s", "<ip>", "-j", "DROP" ] | ||||||
| # ip46tables is a minimal C program (only POSIX dependencies) present as a subdirectory. | # ip46tables is a minimal C program (only POSIX dependencies) present as a subdirectory. | ||||||
| # it permits to handle both ipv4/iptables and ipv6/ip6tables commands | # it permits to handle both ipv4/iptables and ipv6/ip6tables commands | ||||||
|  |  | ||||||
| @ -18,6 +18,20 @@ patterns: | |||||||
|       - 127.0.0.1 |       - 127.0.0.1 | ||||||
|       - ::1 |       - ::1 | ||||||
|  |  | ||||||
|  | # Those commands will be executed in order at start, before everything else | ||||||
|  | start: | ||||||
|  |   - [ "ip46tables", "-w", "-N", "reaction" ] | ||||||
|  |   - [ "ip46tables", "-w", "-A", "reaction", "-j", "ACCEPT" ] | ||||||
|  |   - [ "ip46tables", "-w", "-I", "reaction", "1", "-s", "127.0.0.1", "-j", "ACCEPT" ] | ||||||
|  |   - [ "ip46tables", "-w", "-I", "INPUT", "-p", "all", "-j", "reaction" ] | ||||||
|  |  | ||||||
|  | # Those commands will be executed in order at stop, after everything else | ||||||
|  | stop: | ||||||
|  |   - [ "ip46tables", "-w,", "-D", "INPUT", "-p", "all", "-j", "reaction" ] | ||||||
|  |   - [ "ip46tables", "-w", "-F", "reaction" ] | ||||||
|  |   - [ "ip46tables", "-w", "-X", "reaction" ] | ||||||
|  |  | ||||||
|  |  | ||||||
| # streams are commands | # streams are commands | ||||||
| # they're run and their ouptut is captured | # they're run and their ouptut is captured | ||||||
| # *example:* `tail -f /var/log/nginx/access.log` | # *example:* `tail -f /var/log/nginx/access.log` | ||||||
| @ -27,7 +41,7 @@ streams: | |||||||
|   ssh: |   ssh: | ||||||
|     # note that if the command is not in environment's `PATH` |     # note that if the command is not in environment's `PATH` | ||||||
|     # its full path must be given. |     # its full path must be given. | ||||||
|     cmd: [ "journalctl" "-n0" "-fu" "sshd.service" ] |     cmd: [ "journalctl", "-n0", "-fu", "sshd.service" ] | ||||||
|     # filters run actions when they match regexes on a stream |     # filters run actions when they match regexes on a stream | ||||||
|     filters: |     filters: | ||||||
|       # filters have a user-defined name |       # filters have a user-defined name | ||||||
|  | |||||||
| @ -10,6 +10,8 @@ import ( | |||||||
| type Conf struct { | type Conf struct { | ||||||
| 	Patterns map[string]*Pattern `json:"patterns"` | 	Patterns map[string]*Pattern `json:"patterns"` | ||||||
| 	Streams  map[string]*Stream  `json:"streams"` | 	Streams  map[string]*Stream  `json:"streams"` | ||||||
|  | 	Start    [][]string          `json:"start"` | ||||||
|  | 	Stop     [][]string          `json:"stop"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type Pattern struct { | type Pattern struct { | ||||||
|  | |||||||
| @ -1,8 +1,6 @@ | |||||||
| local directory = '~/.local/share/watch'; |  | ||||||
| // Those strings will be substitued in each shell() call | // Those strings will be substitued in each shell() call | ||||||
| local substitutions = [ | local substitutions = [ | ||||||
|   ['OUTFILE', directory + '/logs-$(date %+F)'], |   ['OUTFILE', '"$HOME/.local/share/watch/logs-$(date +%F)"'], | ||||||
|   ['TMUXFILE', directory + '/tmux'], |  | ||||||
|   ['DATE', '"$(date "+%F %T")"'], |   ['DATE', '"$(date "+%F %T")"'], | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| @ -18,19 +16,17 @@ local shell(prg) = [ | |||||||
|   sub(prg), |   sub(prg), | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| { | local log(line) = shell('echo DATE ' + std.strReplace(line, '\n', ' ') + '>> OUTFILE'); | ||||||
|   // Startup is currently not implemented |  | ||||||
|   startup: shell(||| |  | ||||||
|     mkdir -p "$(dirname OUTFILE)" |  | ||||||
|     echo DATE start >> OUTFILE |  | ||||||
|     # tmux set-hook -g pane-focus-in[50] new-session -d 'echo tmux >> TMUXFILE' |  | ||||||
|   |||), |  | ||||||
|  |  | ||||||
|   // Stop is currently not implemented | { | ||||||
|   stop: shell(||| |   start: [ | ||||||
|     tmux set-hook -ug pane-focus-in[50] |     shell('mkdir -p "$(dirname OUTFILE)"'), | ||||||
|     echo DATE stop >> OUTFILE |     log('start'), | ||||||
|   |||), |   ], | ||||||
|  |  | ||||||
|  |   stop: [ | ||||||
|  |     log('stop'), | ||||||
|  |   ], | ||||||
|  |  | ||||||
|   patterns: { |   patterns: { | ||||||
|     all: { regex: '.*' }, |     all: { regex: '.*' }, | ||||||
| @ -47,7 +43,7 @@ local shell(prg) = [ | |||||||
|         send: { |         send: { | ||||||
|           regex: ['^<all>$'], |           regex: ['^<all>$'], | ||||||
|           actions: { |           actions: { | ||||||
|             send: { cmd: shell('echo DATE focus <all> >> OUTFILE') }, |             send: { cmd: log('focus <all>') }, | ||||||
|           }, |           }, | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
| @ -55,12 +51,13 @@ local shell(prg) = [ | |||||||
|  |  | ||||||
|     // Be notified when user is away |     // Be notified when user is away | ||||||
|     swayidle: { |     swayidle: { | ||||||
|       cmd: ['swayidle', 'timeout', '60', 'echo sleep', 'resume', 'echo resume'], |       // FIXME echo stop and start instead? | ||||||
|  |       cmd: ['swayidle', 'timeout', '30', 'echo sleep', 'resume', 'echo resume'], | ||||||
|       filters: { |       filters: { | ||||||
|         send: { |         send: { | ||||||
|           regex: ['^<all>$'], |           regex: ['^<all>$'], | ||||||
|           actions: { |           actions: { | ||||||
|             send: { cmd: shell('echo DATE <all> >> OUTFILE') }, |             send: { cmd: log('<all>') }, | ||||||
|           }, |           }, | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
| @ -92,7 +89,7 @@ local shell(prg) = [ | |||||||
|     //     send: { |     //     send: { | ||||||
|     //       regex: ['^tmux <all>$'], |     //       regex: ['^tmux <all>$'], | ||||||
|     //       actions: { |     //       actions: { | ||||||
|     //         send: { cmd: shell('echo DATE tmux <all> >> OUTFILE') }, |     //         send: { cmd: log('tmux <all>') }, | ||||||
|     //       }, |     //       }, | ||||||
|     //     }, |     //     }, | ||||||
|     //   }, |     //   }, | ||||||
|  | |||||||
| @ -1,11 +1,13 @@ | |||||||
| // This file is using JSONNET, a complete configuration language based on JSON | // This file is using JSONNET, a complete configuration language based on JSON | ||||||
| // See https://jsonnet.org | // See https://jsonnet.org | ||||||
| // JSONNET is a superset of JSON, so one can write plain JSON files if wanted. | // JSONNET is a superset of JSON, so one can write plain JSON files if wanted. | ||||||
| // Note that YAML is also supported. | // Note that YAML is also supported, see ./example.yml | ||||||
|  |  | ||||||
|  | // A JSONNET function | ||||||
|  | local iptables(args) = ['ip46tables', '-w'] + args; | ||||||
| // variables defined for later use. | // variables defined for later use. | ||||||
| local iptablesban = ['ip46tables', '-w', '-A', 'reaction', '1', '-s', '<ip>', '-j', 'DROP']; | local iptablesban = iptables(['-A', 'reaction', '1', '-s', '<ip>', '-j', 'drop']); | ||||||
| local iptablesunban = ['ip46tables', '-w', '-D', 'reaction', '1', '-s', '<ip>', '-j', 'DROP']; | local iptablesunban = iptables(['-D', 'reaction', '1', '-s', '<ip>', '-j', 'drop']); | ||||||
| // ip46tables is a minimal C program (only POSIX dependencies) present as a subdirectory. | // ip46tables is a minimal C program (only POSIX dependencies) present as a subdirectory. | ||||||
| // it permits to handle both ipv4/iptables and ipv6/ip6tables commands | // it permits to handle both ipv4/iptables and ipv6/ip6tables commands | ||||||
|  |  | ||||||
| @ -21,6 +23,30 @@ local iptablesunban = ['ip46tables', '-w', '-D', 'reaction', '1', '-s', '<ip>', | |||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|  |   // Those commands will be executed in order at start, before everything else | ||||||
|  |   start: [ | ||||||
|  |     // Create an iptables chain for reaction | ||||||
|  |     iptables(['-N', 'reaction']), | ||||||
|  |     // Set its default to ACCEPT | ||||||
|  |     iptables(['-A', 'reaction', '-j', 'ACCEPT']), | ||||||
|  |     // Always accept 127.0.0.1 | ||||||
|  |     iptables(['-I', 'reaction', '1', '-s', '127.0.0.1', '-j', 'ACCEPT']), | ||||||
|  |     // Always accept ::1 | ||||||
|  |     iptables(['-I', 'reaction', '1', '-s', '::1', '-j', 'ACCEPT']), | ||||||
|  |     // Insert this chain as the first item of the INPUT chain (for incoming connections) | ||||||
|  |     iptables(['-I', 'INPUT', '-p', 'all', '-j', 'reaction']), | ||||||
|  |   ], | ||||||
|  |  | ||||||
|  |   // Those commands will be executed in order at stop, after everything else | ||||||
|  |   stop: [ | ||||||
|  |     // Remove the chain from the INPUT chain | ||||||
|  |     iptables(['-D,', 'INPUT', '-p', 'all', '-j', 'reaction']), | ||||||
|  |     // Empty the chain | ||||||
|  |     iptables(['-F,', 'reaction']), | ||||||
|  |     // Delete the chain | ||||||
|  |     iptables(['-X,', 'reaction']), | ||||||
|  |   ], | ||||||
|  |  | ||||||
|   // streams are commands |   // streams are commands | ||||||
|   // they're run and their ouptut is captured |   // they're run and their ouptut is captured | ||||||
|   // *example:* `tail -f /var/log/nginx/access.log` |   // *example:* `tail -f /var/log/nginx/access.log` | ||||||
|  | |||||||
| @ -6,24 +6,6 @@ WantedBy=multi-user.target | |||||||
| [Service] | [Service] | ||||||
| ExecStart=/path/to/reaction -c /etc/reaction.yml | ExecStart=/path/to/reaction -c /etc/reaction.yml | ||||||
|  |  | ||||||
| # Create an iptables chain for reaction |  | ||||||
| ExecStartPre=/path/to/ip46tables -w -N reaction |  | ||||||
| # Set its default to ACCEPT |  | ||||||
| ExecStartPre=/path/to/ip46tables -w -A reaction -j ACCEPT |  | ||||||
| # Always accept 127.0.0.1 |  | ||||||
| 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/ip46tables -w -I INPUT -p all -j reaction |  | ||||||
|  |  | ||||||
| # Remove the chain from the INPUT chain |  | ||||||
| ExecStopPost=/path/to/ip46tables -w -D INPUT -p all -j reaction |  | ||||||
| # Empty the chain |  | ||||||
| ExecStopPost=/path/to/ip46tables -w -F reaction |  | ||||||
| # Delete the chain |  | ||||||
| ExecStopPost=/path/to/ip46tables -w -X reaction |  | ||||||
|  |  | ||||||
| # Ask systemd to create /var/lib/reaction (/var/lib/ is implicit) | # Ask systemd to create /var/lib/reaction (/var/lib/ is implicit) | ||||||
| StateDirectory=reaction | StateDirectory=reaction | ||||||
| # Ask systemd to create /run/reaction at runtime (/run/ is implicit) | # Ask systemd to create /run/reaction at runtime (/run/ is implicit) | ||||||
|  | |||||||
| @ -69,8 +69,10 @@ func Printf(level Level, format string, args ...any) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func Fatalln(args ...any) { | func Fatalln(args ...any) { | ||||||
| 	level := FATAL | 	newargs := make([]any, 0) | ||||||
| 	log.Fatalln(level.String(), args) | 	newargs = append(newargs, FATAL) | ||||||
|  | 	newargs = append(newargs, args...) | ||||||
|  | 	log.Fatalln(newargs...) | ||||||
| } | } | ||||||
|  |  | ||||||
| func Fatalf(format string, args ...any) { | func Fatalf(format string, args ...any) { | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 ppom
					ppom