mirror of
https://github.com/clearlinux/tallow.git
synced 2026-06-16 01:15:48 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dccbfce478 |
@@ -1,23 +0,0 @@
|
||||
name: C/C++ CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: install dependencies
|
||||
run: sudo apt-get install check libjson-c-dev libpcre2-dev libsystemd-dev
|
||||
- name: autogen
|
||||
run: sh autogen.sh
|
||||
- name: configure
|
||||
run: ./configure
|
||||
- name: make
|
||||
run: make
|
||||
- name: make check
|
||||
run: make check
|
||||
- name: make distcheck
|
||||
run: make distcheck
|
||||
+2
-2
@@ -10,9 +10,9 @@ depcomp
|
||||
install-sh
|
||||
missing
|
||||
tallow
|
||||
tallow-*.tar.*
|
||||
tallow-*.tar.gz
|
||||
tallow-*/
|
||||
*.o
|
||||
tallow.o
|
||||
tallow.service
|
||||
*~
|
||||
DEADJOE
|
||||
|
||||
@@ -16,7 +16,6 @@ addons:
|
||||
- valgrind
|
||||
- autoconf
|
||||
- automake
|
||||
- libjson-c-dev
|
||||
|
||||
script:
|
||||
- ./configure && make && make distcheck
|
||||
|
||||
+11
-29
@@ -1,44 +1,26 @@
|
||||
|
||||
AM_CFLAGS = -g \
|
||||
$(JSON_C_CFLAGS) $(PCRE_CFLAGS) $(LIBSYSTEMD_CFLAGS) \
|
||||
-Wall -Wno-uninitialized -W -D_FORTIFY_SOURCE=2
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-DDATADIR='"$(datadir)"' -DSYSCONFDIR='"$(sysconfdir)"'
|
||||
AM_CFLAGS = -g $(PCRE_CFLAGS) $(LIBSYSTEMD_CFLAGS) -Wall -Wno-uninitialized -W -D_FORTIFY_SOURCE=2
|
||||
|
||||
systemdsystemunitdir = @SYSTEMD_SYSTEMUNITDIR@
|
||||
systemdsystemunit_DATA = data/tallow.service
|
||||
systemdsystemunit_DATA = tallow.service
|
||||
|
||||
sbin_PROGRAMS = tallow
|
||||
tallow_SOURCES = \
|
||||
src/tallow.c \
|
||||
src/json.c \
|
||||
src/json.h \
|
||||
src/data.c \
|
||||
src/data.h
|
||||
tallow_LDADD = $(JSON_C_LIBS) $(PCRE_LIBS) $(LIBSYSTEMD_LIBS)
|
||||
tallow_SOURCES = tallow.c
|
||||
tallow_LDADD = $(PCRE_LIBS) $(LIBSYSTEMD_LIBS)
|
||||
|
||||
pkgdata_DATA = data/sshd.json
|
||||
EXTRA_DIST = AUTHORS COPYING INSTALL README.md tallow.service.in tallow.conf.5.md tallow.1.md
|
||||
|
||||
EXTRA_DIST = \
|
||||
AUTHORS COPYING INSTALL README.md \
|
||||
data/tallow.service.in \
|
||||
data/sshd.json \
|
||||
man/tallow.conf.5.md \
|
||||
man/tallow.patterns.5.md \
|
||||
man/tallow.1.md
|
||||
|
||||
dist_man_MANS = man/tallow.1 man/tallow.conf.5 man/tallow.patterns.5
|
||||
dist_man_MANS = tallow.1 tallow.conf.5
|
||||
|
||||
dist_doc_DATA = tallow.conf
|
||||
|
||||
DISTCHECK_CONFIGURE_FLAGS = \
|
||||
--with-systemdsystemunitdir=$(DESTDIR)$(SYSTEMDSYSTEMUNITDIR)
|
||||
|
||||
docs: $(dist_man_MANS)
|
||||
man/%.5: man/%.5.md
|
||||
ronn -r $< --pipe > $@
|
||||
docs: tallow.1 tallow.conf.5
|
||||
tallow.conf.5: tallow.conf.5.md
|
||||
ronn -r tallow.conf.5.md --pipe > tallow.conf.5
|
||||
|
||||
man/%.1: man/%.1.md
|
||||
ronn -r $< --pipe > $@
|
||||
tallow.1: tallow.1.md
|
||||
ronn -r tallow.1.md --pipe > tallow.1
|
||||
|
||||
|
||||
@@ -16,9 +16,7 @@ Tallow attaches to the journal and subscribes to messages from
|
||||
/usr/sbin/sshd. The messages are matched against rules and the IP
|
||||
address is extracted from the message. For each IP address that is
|
||||
extracted, the last timestamp and count is kept. Once the count exceeds
|
||||
a threshold, the offending IP address is added to an ipset and blocked
|
||||
with a corresponding firewall rule. It will use firewalld or
|
||||
iptables / ip6tables.
|
||||
a threshold, iptables is executed to set a IP-based blocking rule.
|
||||
|
||||
The timestamp is kept for pruning. Records are pruned from the list
|
||||
if the IP address hasn't been seen by tallow for longer than the
|
||||
@@ -68,4 +66,4 @@ tallow.
|
||||
|
||||
Be very careful if you deploy tallow on systems that expect valid
|
||||
users to log on from many random source addresses. If your user
|
||||
mistypes their username, they could find themselves denied access.
|
||||
mistypes their username, they could find themselves denied access.
|
||||
|
||||
+3
-5
@@ -2,9 +2,8 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ([2.64])
|
||||
AC_INIT([tallow], [21], [auke-jan.h.kok@intel.com])
|
||||
AM_INIT_AUTOMAKE([foreign -Wall -Werror -Wno-portability silent-rules subdir-objects color-tests
|
||||
no-dist-gzip dist-xz])
|
||||
AC_INIT([tallow], [12], [auke-jan.h.kok@intel.com])
|
||||
AM_INIT_AUTOMAKE([foreign])
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
|
||||
# Checks for programs.
|
||||
@@ -12,7 +11,6 @@ AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
|
||||
PKG_CHECK_MODULES(PCRE, libpcre)
|
||||
PKG_CHECK_MODULES(JSON_C, json-c)
|
||||
PKG_CHECK_MODULES(LIBSYSTEMD, libsystemd,, [PKG_CHECK_MODULES(LIBSYSTEMD, libsystemd-journal)])
|
||||
AC_SUBST(LIBSYSTEMD_CFLAGS)
|
||||
AC_SUBST(LIBSYSTEMD_LIBS)
|
||||
@@ -30,5 +28,5 @@ fi
|
||||
AC_CHECK_HEADERS([stdlib.h stdio.h string.h stdarg.h limits.h sys/time.h])
|
||||
|
||||
AC_OUTPUT([
|
||||
data/tallow.service
|
||||
tallow.service
|
||||
])
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
[
|
||||
{
|
||||
"filter": "SYSLOG_IDENTIFIER=auth",
|
||||
"items": [
|
||||
{
|
||||
"ban": 50,
|
||||
"score": 0.6,
|
||||
"pattern": "MESSAGE=pam_unix[(]dovecot:auth[)]: authentication failure; logname= uid=0 euid=0 tty=dovecot ruser=.*@.* rhost=([0-9a-z:.]+)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -1,57 +0,0 @@
|
||||
[
|
||||
{
|
||||
"filter": "SYSLOG_IDENTIFIER=sshd",
|
||||
"items": [
|
||||
{
|
||||
"ban": 0,
|
||||
"score": 0.2,
|
||||
"pattern": "MESSAGE=Failed .* for .* from ([0-9a-z:.]+) port \\d+ ssh2"
|
||||
},
|
||||
{
|
||||
"ban": 0,
|
||||
"score": 0.2,
|
||||
"pattern": "MESSAGE=error: PAM: Authentication failure for .* from ([0-9a-z:.]+)"
|
||||
},
|
||||
{
|
||||
"ban": 10,
|
||||
"score": 0.2,
|
||||
"pattern": "MESSAGE=Invalid user .* from ([0-9a-z:.]+) port \\d+"
|
||||
},
|
||||
{
|
||||
"ban": 10,
|
||||
"score": 0.3,
|
||||
"pattern": "MESSAGE=Did not receive identification string from ([0-9a-z:.]+) port \\d+"
|
||||
},
|
||||
{
|
||||
"ban": 15,
|
||||
"score": 0.4,
|
||||
"pattern": "MESSAGE=Bad protocol version identification .* from ([0-9a-z:.]+)"
|
||||
},
|
||||
{
|
||||
"ban": 15,
|
||||
"score": 0.4,
|
||||
"pattern": "MESSAGE=Connection closed by authenticating user .* ([0-9a-z:.]+) port \\d+"
|
||||
},
|
||||
{
|
||||
"ban": 10,
|
||||
"score": 0.3,
|
||||
"pattern": "MESSAGE=Received disconnect from ([0-9a-z:.]+) port .*\\[preauth\\]"
|
||||
},
|
||||
{
|
||||
"ban": 10,
|
||||
"score": 0.3,
|
||||
"pattern": "MESSAGE=Connection closed by ([0-9a-z:.]+) port .*\\[preauth\\]"
|
||||
},
|
||||
{
|
||||
"ban": 30,
|
||||
"score": 0.5,
|
||||
"pattern": "MESSAGE=Failed .* for root from ([0-9a-z:.]+) port \\d+ ssh2"
|
||||
},
|
||||
{
|
||||
"ban": 60,
|
||||
"score": 0.6,
|
||||
"pattern": "MESSAGE=Unable to negotiate with ([0-9a-z:.]+) port \\d+: no matching key exchange method found."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -1,94 +0,0 @@
|
||||
|
||||
## tallow.conf
|
||||
|
||||
The tallow configuration file
|
||||
|
||||
## NAME
|
||||
|
||||
tallow.conf - Tallow daemon configuration file
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`/etc/tallow.conf`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
This file is read on startup by the tallow(1) daemon, and can
|
||||
be used to provide options to the tallow daemon. If not present,
|
||||
tallow will operate with built-in defaults.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
`fwcmd_path`=`<string>`
|
||||
Specifies the location of the ipset(1) firewall-cmd(1) programs. By
|
||||
default, tallow will look in "/usr/sbin" for them.
|
||||
|
||||
`ipt_path`=`<string>`
|
||||
Specifies the location of the ipset(1) program and iptables(1) or
|
||||
ip6tables(1) programs. By default, tallow will look in "/usr/sbin"
|
||||
for them.
|
||||
|
||||
`expires`=`<int>`
|
||||
The number of seconds that IP addresses are blocked for. Note that
|
||||
due to the implementation, IP addresses may be blocked for much
|
||||
longer than this period. If IP addresses are seen, but not
|
||||
blocked within this period, they are also removed from the
|
||||
watch list. Defaults to 3600s.
|
||||
|
||||
`whitelist`=`<ip address|pattern>`
|
||||
Specify an IP address or `pattern` that should never be
|
||||
blocked. Multiple IP addresses can be included by repeating the
|
||||
`whitelist` option several times. By default, 127.0.0.1, 192.168., and
|
||||
10. are whitelisted. If you create a manual whitelist, you must include
|
||||
these entries if you want to continue them to be whitelisted as
|
||||
well, otherwise they will be omitted from the whitelist.
|
||||
|
||||
If the last character of the listed ip adress is a `.` or a `:`, then
|
||||
the matching is only performed on the leftmost characters of an IP
|
||||
address against the whitelist entry. For instance, if you whitelist
|
||||
`10.` then all IP addresses in the `10/8` subnet mask will match this
|
||||
whitelist entry and never be blocked.
|
||||
|
||||
`ipv6`=`<0|1>`
|
||||
Enable or disable ipv6 (ip6tables) support. Ipv6 is disabled
|
||||
automatically on systems that do not appear to have ipv6 support
|
||||
and enabled when ipv6 is present. Use this option to explicitly
|
||||
disable ipv6 support if your system does not have ipv6 or is
|
||||
missing ip6tables. Even with ipv6 disabled, tallow will track
|
||||
and log ipv6 addresses.
|
||||
|
||||
`nocreate`=`<0|1>` Disable the creation of firewall rules and ipset sets. By
|
||||
default, tallow will create new firewall-cmd(1) or iptables(1) and ip6tables(1)
|
||||
rules when needed automatically. If set to `1`, `tallow(1)` will not create any
|
||||
new firewall DROP rules or ipset sets that are needed work. You should create
|
||||
them manually before tallow starts up and remove them afterwards using the sets
|
||||
of commands below.
|
||||
|
||||
Use the following commands if you're using iptables(1):
|
||||
|
||||
```
|
||||
ipset create tallow hash:ip family inet timeout 3600
|
||||
iptables -t filter -I INPUT 1 -m set --match-set tallow src -j DROP
|
||||
|
||||
ipset create tallow6 hash:ip family inet6 timeout 3600
|
||||
ip6tables -t filter -I INPUT 1 -m set --match-set tallow6 src -j DROP
|
||||
```
|
||||
|
||||
Use the following commands if you're using firewalld(1):
|
||||
|
||||
```
|
||||
firewall-cmd --permanent --new-ipset=tallow --type=hash:ip --family=inet --option=timeout=3600
|
||||
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 -m set --match-set tallow src -j DROP
|
||||
|
||||
firewall-cmd --permanent --new-ipset=tallow6 --type=hash:ip --family=inet6 --option=timeout=3600
|
||||
firewall-cmd --permanent --direct --add-rule ipv6 filter INPUT 1 -m set --match-set tallow6 src -j DROP
|
||||
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
|
||||
tallow(1), tallow.patterns(5)
|
||||
|
||||
## AUTHOR
|
||||
|
||||
Auke Kok <auke-jan.h.kok@intel.com>
|
||||
@@ -1,128 +0,0 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "TALLOW" "5" "February 2020" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBtallow\fR
|
||||
.
|
||||
.SH "tallow\.patterns"
|
||||
Tallow pattern matching configuration files\.
|
||||
.
|
||||
.SH "SYNOPSIS"
|
||||
tallow(1) uses regular expressions to match journal entries and extract an IP address from them\. JSON files are used to configure the patterns and banning thresholds used by tallow(1)\.
|
||||
.
|
||||
.P
|
||||
\fB/etc/tallow/*\.json\fR \fB/usr/share/tallow/*\.json\fR
|
||||
.
|
||||
.SH "DESCRIPTION"
|
||||
tallow(1) uses regular expressions to match journal entries and extract an IP address from them\. JSON files are used to configure the patterns and banning thresholds used by tallow(1)\. This adds the ability to extend the patterns tallow(1) will recognize\. Many JSON files can exist for logical grouping\. The tallow(1) daemon will read all JSON files in the configuration directories at startup\.
|
||||
.
|
||||
.P
|
||||
tallow(1) operates with default pattern definitions in\fB/usr/share/tallow/*\.json\fR\. Users can add more patterns with their own JSON files under \fB/etc/tallow\fR\. The default JSON files can be overridden by creating the same file under \fB/etc/tallow\fR\.
|
||||
.
|
||||
.SH "FILE FORMAT"
|
||||
Pattern configuration files use the JavaScript Object Notation (JSON) format\.
|
||||
.
|
||||
.P
|
||||
The JSON must be two levels deep and all properties are required\. The root object is an array containing objects with a \fBfilter\fR key and an \fBitems\fR key\.
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
\fBfilter\fR is a string that defines a field for filtering the journal file\. This helps make sure patterns are only matched to a subset of journal entries\. See systemd\.journal\-fields(7) for valid journal fields\.
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
\fBitems\fR is an array of objects that contains three elements: \fBban\fR, \fBscore\fR, and \fBpattern\fR\.
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
\fBban\fR is an integer that defines the number of seconds to ban originating IP for\. If this value is > 0, the IP address get banned immediately when a journal entry matches \fBpattern\fR\.
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
\fBscore\fR is a double that defines a value to add to the accumulated "score" of an originating IP address each time a journal entry matches the \fBpattern\fR\. If the combined score is > 1\.0, tallow bans the originating IP for the default time of 1 hour\. The \fBban\fR element value above is not used for bans made due to \fBscore\fR\.
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
\fBpattern\fR is a string that defines a Perl Compatible Regular Expressions (PCRE) to match against the filtered journal entries\. The PCRE should extract exactly one substring: the originating IP address for tallow(1)\. See systemd\.journal\-fields(7) for valid journal fields\.
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "EXAMPLES"
|
||||
.
|
||||
.IP "1." 4
|
||||
The JSON below is a snippet from one of the default pattern configuration files for blocking certain failed \fBsshd\fR connections\.
|
||||
.
|
||||
.IP
|
||||
The first pattern will ban an IP address after it fails to login 6 times causing it to reach a total score > 1\.0\.
|
||||
.
|
||||
.IP
|
||||
The second pattern will ban an IP address for 10 seconds every time a login is attempted with an invalid user\. Additionally, it will ban the IP address for 1 hour if it attempts to login with an invalid user 6 times causing it to reach a total score > 1\.0\.
|
||||
.
|
||||
.IP
|
||||
See the \fB/usr/share/tallow/sshd\.json\fR file for more \fBsshd\fR examples\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
[
|
||||
{
|
||||
"filter": "SYSLOG_IDENTIFIER=sshd",
|
||||
"items": [
|
||||
{
|
||||
"ban": 0,
|
||||
"score": 0\.2,
|
||||
"pattern": "MESSAGE=Failed \.* for \.* from ([0\-9a\-z:\.]+) port \e\ed+ ssh2"
|
||||
},
|
||||
{
|
||||
"ban": 10,
|
||||
"score": 0\.2,
|
||||
"pattern": "MESSAGE=Invalid user \.* from ([0\-9a\-z:\.]+) port \e\ed+"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
.
|
||||
.IP "2." 4
|
||||
The JSON below defines a pattern for blocking connections based on error logs from \fBnginx\-mainline\fR if placed in a \fB/etc/tallow/nginx\-mainline\.json\fR file\.
|
||||
.
|
||||
.IP
|
||||
The pattern will ban an IP address for 15 seconds every time it attempts to access a script that does not exist\. Additionally, it will ban the IP address for 1 hour if it attempts to access invalid scripts 4 times causing it to reach a total score > 1\.0\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
[
|
||||
{
|
||||
"filter": "SYSLOG_IDENTIFIER=nginx\-mainline",
|
||||
"items": [
|
||||
{
|
||||
"ban": 15,
|
||||
"score": 0\.3,
|
||||
"pattern": "\.Primary script unknown\. while reading response header from upstream, client: ([0\-9a\-z:\.]+),"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "SEE ALSO"
|
||||
tallow(1), tallow\.conf(5)
|
||||
.
|
||||
.SH "BUGS"
|
||||
\fBtallow\fR is \fBNOT A SECURITY SOLUTION\fR, nor does it protect against random password logins\. An attacker may still be able to logon to your systems if you allow password logins\.
|
||||
.
|
||||
.SH "AUTHOR"
|
||||
Auke Kok \fIauke\-jan\.h\.kok@intel\.com\fR
|
||||
@@ -1,134 +0,0 @@
|
||||
## tallow.patterns
|
||||
|
||||
Tallow pattern matching configuration files.
|
||||
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
tallow(1) uses regular expressions to match journal entries and extract an IP
|
||||
address from them. JSON files are used to configure the patterns and banning
|
||||
thresholds used by tallow(1).
|
||||
|
||||
`/etc/tallow/*.json`
|
||||
`/usr/share/tallow/*.json`
|
||||
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
tallow(1) uses regular expressions to match journal entries and extract an IP
|
||||
address from them. JSON files are used to configure the patterns and banning
|
||||
thresholds used by tallow(1). This adds the ability to extend the patterns
|
||||
tallow(1) will recognize. Many JSON files can exist for logical grouping. The
|
||||
tallow(1) daemon will read all JSON files in the configuration directories at
|
||||
startup.
|
||||
|
||||
tallow(1) operates with default pattern definitions
|
||||
in`/usr/share/tallow/*.json`. Users can add more patterns with their own JSON
|
||||
files under `/etc/tallow`. The default JSON files can be overridden by creating
|
||||
the same file under `/etc/tallow`.
|
||||
|
||||
|
||||
## FILE FORMAT
|
||||
|
||||
Pattern configuration files use the JavaScript Object Notation (JSON) format.
|
||||
|
||||
The JSON must be two levels deep and all properties are required. The root
|
||||
object is an array containing objects with a `filter` key and an `items` key.
|
||||
|
||||
* `filter` is a string that defines a field for filtering the journal file.
|
||||
This helps make sure patterns are only matched to a subset of journal
|
||||
entries. See systemd.journal-fields(7) for valid journal fields.
|
||||
|
||||
* `items` is an array of objects that contains three elements: `ban`, `score`,
|
||||
and `pattern`.
|
||||
|
||||
* `ban` is an integer that defines the number of seconds to ban originating
|
||||
IP for. If this value is > 0, the IP address get banned immediately when a
|
||||
journal entry matches `pattern`.
|
||||
|
||||
* `score` is a double that defines a value to add to the accumulated "score"
|
||||
of an originating IP address each time a journal entry matches
|
||||
the `pattern`. If the combined score is > 1.0, tallow bans the originating
|
||||
IP for the default time of 1 hour. The `ban` element value above is not
|
||||
used for bans made due to `score`.
|
||||
|
||||
* `pattern` is a string that defines a Perl Compatible Regular Expressions
|
||||
(PCRE) to match against the filtered journal entries. The PCRE should
|
||||
extract exactly one substring: the originating IP address for tallow(1).
|
||||
See systemd.journal-fields(7) for valid journal fields.
|
||||
|
||||
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
1. The JSON below is a snippet from one of the default pattern configuration
|
||||
files for blocking certain failed `sshd` connections.
|
||||
|
||||
The first pattern will ban an IP address after it fails to login 6 times
|
||||
causing it to reach a total score > 1.0.
|
||||
|
||||
The second pattern will ban an IP address for 10 seconds every time a login is
|
||||
attempted with an invalid user. Additionally, it will ban the IP address for
|
||||
1 hour if it attempts to login with an invalid user 6 times causing it to
|
||||
reach a total score > 1.0.
|
||||
|
||||
See the `/usr/share/tallow/sshd.json` file for more `sshd` examples.
|
||||
|
||||
```
|
||||
[
|
||||
{
|
||||
"filter": "SYSLOG_IDENTIFIER=sshd",
|
||||
"items": [
|
||||
{
|
||||
"ban": 0,
|
||||
"score": 0.2,
|
||||
"pattern": "MESSAGE=Failed .* for .* from ([0-9a-z:.]+) port \\d+ ssh2"
|
||||
},
|
||||
{
|
||||
"ban": 10,
|
||||
"score": 0.2,
|
||||
"pattern": "MESSAGE=Invalid user .* from ([0-9a-z:.]+) port \\d+"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
|
||||
2. The JSON below defines a pattern for blocking connections based on error logs
|
||||
from `nginx-mainline` if placed in a `/etc/tallow/nginx-mainline.json` file.
|
||||
|
||||
The pattern will ban an IP address for 15 seconds every time it attempts to
|
||||
access a script that does not exist. Additionally, it will ban the IP
|
||||
address for 1 hour if it attempts to access invalid scripts 4 times causing
|
||||
it to reach a total score > 1.0.
|
||||
|
||||
```
|
||||
[
|
||||
{
|
||||
"filter": "SYSLOG_IDENTIFIER=nginx-mainline",
|
||||
"items": [
|
||||
{
|
||||
"ban": 15,
|
||||
"score": 0.3,
|
||||
"pattern": ".Primary script unknown. while reading response header from upstream, client: ([0-9a-z:.]+),"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
|
||||
tallow(1), tallow.conf(5)
|
||||
|
||||
## BUGS
|
||||
|
||||
`tallow` is `NOT A SECURITY SOLUTION`, nor does it protect against random
|
||||
password logins. An attacker may still be able to logon to your systems if you
|
||||
allow password logins.
|
||||
|
||||
## AUTHOR
|
||||
|
||||
Auke Kok <auke-jan.h.kok@intel.com>
|
||||
-175
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* data.h - IP block sshd login abuse
|
||||
*
|
||||
* (C) Copyright 2019 Intel Corporation
|
||||
* Authors:
|
||||
* Auke Kok <auke-jan.h.kok@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 3
|
||||
* of the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <pcre.h>
|
||||
|
||||
#include "data.h"
|
||||
|
||||
struct block_struct *blocks;
|
||||
struct pattern_struct *patterns;
|
||||
struct filter_struct *filters;
|
||||
struct whitelist_struct *whitelist;
|
||||
|
||||
void filter_add(const char *filter)
|
||||
{
|
||||
struct filter_struct *h = filters;
|
||||
|
||||
/* filter duplicates */
|
||||
while (h) {
|
||||
if (strcmp(h->filter, filter) == 0)
|
||||
return;
|
||||
|
||||
h = h->next;
|
||||
}
|
||||
|
||||
struct filter_struct *f = calloc(1, sizeof(struct filter_struct));
|
||||
if (!f) {
|
||||
perror("calloc()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
f->filter = strdup(filter);
|
||||
|
||||
h = filters;
|
||||
if (!h) {
|
||||
filters = f;
|
||||
} else {
|
||||
while (h->next)
|
||||
h = h->next;
|
||||
h->next = f;
|
||||
}
|
||||
}
|
||||
|
||||
void pattern_add(const char *pattern, int ban, double score)
|
||||
{
|
||||
struct pattern_struct *p = calloc(1, sizeof(struct pattern_struct));
|
||||
if (!p) {
|
||||
perror("calloc()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
p->pattern = strdup(pattern);
|
||||
p->instant_block = ban;
|
||||
p->weight = score;
|
||||
|
||||
const char *pcre_err;
|
||||
int err;
|
||||
p->re = pcre_compile(pattern, 0, &pcre_err, &err, NULL);
|
||||
if (!p->re) {
|
||||
fprintf(stderr, "PCRE compilation failed. Offset %d: %s\n",
|
||||
err, pcre_err);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
struct pattern_struct *h = patterns;
|
||||
if (!h) {
|
||||
patterns = p;
|
||||
} else {
|
||||
while (h->next)
|
||||
h = h->next;
|
||||
h->next = p;
|
||||
}
|
||||
}
|
||||
|
||||
void whitelist_add(const char *ip)
|
||||
{
|
||||
struct whitelist_struct *w = calloc(1, sizeof(struct whitelist_struct));
|
||||
|
||||
if (!w) {
|
||||
perror("calloc()");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
w->ip = strdup(ip);
|
||||
|
||||
size_t l = strlen(ip);
|
||||
if ((ip[l-1] == '.') || (ip[l-1] == ':'))
|
||||
w->len = l;
|
||||
else
|
||||
w->len = -1;
|
||||
|
||||
struct whitelist_struct *h = whitelist;
|
||||
if (!h) {
|
||||
whitelist = w;
|
||||
} else {
|
||||
while (h->next)
|
||||
h = h->next;
|
||||
h->next = w;
|
||||
}
|
||||
}
|
||||
|
||||
bool whitelist_find(const char *ip)
|
||||
{
|
||||
struct whitelist_struct *w = whitelist;
|
||||
while (w) {
|
||||
if (w->len > 0) {
|
||||
if (!strncmp(w->ip, ip, w->len))
|
||||
return (true);
|
||||
} else {
|
||||
if (!strcmp(w->ip, ip))
|
||||
return (true);
|
||||
}
|
||||
w = w->next;
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
void prune(int expires)
|
||||
{
|
||||
struct block_struct *s = blocks;
|
||||
struct block_struct *p;
|
||||
struct timeval tv;
|
||||
|
||||
(void) gettimeofday(&tv, NULL);
|
||||
p = NULL;
|
||||
|
||||
while (s) {
|
||||
/*
|
||||
* Expire all records, but if they are blocked, make sure to
|
||||
* expire them *before* the ipset rule expires, otherwise
|
||||
* you might get an IP to bypass checks.
|
||||
*/
|
||||
time_t age = tv.tv_sec - s->time.tv_sec;
|
||||
if ((age > expires) ||
|
||||
((s->blocked) && (age > expires / 2))) {
|
||||
dbg("Expired record for %s\n", s->ip);
|
||||
if (p) {
|
||||
p->next = s->next;
|
||||
free(s->ip);
|
||||
free(s);
|
||||
s = p->next;
|
||||
continue;
|
||||
} else {
|
||||
blocks = s->next;
|
||||
free(s->ip);
|
||||
free(s);
|
||||
s = blocks;
|
||||
p = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
p = s;
|
||||
s = s->next;
|
||||
}
|
||||
|
||||
/* return some memory */
|
||||
malloc_trim(0);
|
||||
}
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* data.h - IP block sshd login abuse
|
||||
*
|
||||
* (C) Copyright 2019 Intel Corporation
|
||||
* Authors:
|
||||
* Auke Kok <auke-jan.h.kok@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 3
|
||||
* of the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pcre.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define dbg(args...) fprintf(stderr, ##args)
|
||||
#else
|
||||
#define dbg(args...) do {} while (0)
|
||||
#endif
|
||||
|
||||
struct block_struct {
|
||||
char *ip;
|
||||
float score;
|
||||
struct timeval time;
|
||||
struct block_struct *next;
|
||||
bool blocked;
|
||||
};
|
||||
|
||||
struct whitelist_struct {
|
||||
char *ip;
|
||||
size_t len;
|
||||
struct whitelist_struct *next;
|
||||
};
|
||||
|
||||
struct pattern_struct {
|
||||
int instant_block;
|
||||
float weight;
|
||||
char *pattern;
|
||||
pcre *re;
|
||||
struct pattern_struct *next;
|
||||
};
|
||||
|
||||
struct filter_struct {
|
||||
char *filter;
|
||||
struct filter_struct *next;
|
||||
};
|
||||
|
||||
extern struct block_struct *blocks;
|
||||
extern struct pattern_struct *patterns;
|
||||
extern struct filter_struct *filters;
|
||||
extern struct whitelist_struct *whitelist;
|
||||
|
||||
void filter_add(const char *filter);
|
||||
void pattern_add(const char *pattern, int ban, double score);
|
||||
void whitelist_add(const char *ip);
|
||||
bool whitelist_find(const char *ip);
|
||||
void prune(int expires);
|
||||
-218
@@ -1,218 +0,0 @@
|
||||
/*
|
||||
* json.c - IP block sshd login abuse
|
||||
*
|
||||
* (C) Copyright 2019 Intel Corporation
|
||||
* Authors:
|
||||
* Auke Kok <auke-jan.h.kok@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 3
|
||||
* of the License.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <json-c/json.h>
|
||||
|
||||
#include "data.h"
|
||||
|
||||
static const char *s_pattern;
|
||||
static const char *s_filter;
|
||||
static int s_ban;
|
||||
static double s_score;
|
||||
static bool g_filter, g_ban, g_score, g_pattern = false;
|
||||
|
||||
static int json_parse(json_object *ob)
|
||||
{
|
||||
int i, count = 0;
|
||||
bool l_filter, l_ban, l_score, l_pattern = false;
|
||||
|
||||
json_object_object_foreach(ob, key, val) {
|
||||
enum json_type type;
|
||||
type = json_object_get_type(val);
|
||||
switch (type) {
|
||||
case json_type_null:
|
||||
break;
|
||||
case json_type_boolean:
|
||||
break;
|
||||
case json_type_double:
|
||||
if (strcmp(key, "score") == 0) {
|
||||
l_score = g_score = true;
|
||||
s_score = json_object_get_double(val);
|
||||
} else {
|
||||
fprintf(stderr, "Invalid JSON key \"%s\"\n", key);
|
||||
return (0);
|
||||
}
|
||||
break;
|
||||
case json_type_int:
|
||||
if (strcmp(key, "ban") == 0) {
|
||||
l_ban = g_ban = true;
|
||||
s_ban = json_object_get_int(val);
|
||||
} else {
|
||||
fprintf(stderr, "Invalid JSON key \"%s\"\n", key);
|
||||
return (0);
|
||||
}
|
||||
break;
|
||||
case json_type_string:
|
||||
if (strcmp(key, "filter") == 0) {
|
||||
l_filter = g_filter = true;
|
||||
s_filter = json_object_get_string(val);
|
||||
} else if (strcmp(key, "pattern") == 0) {
|
||||
l_pattern = g_pattern = true;
|
||||
s_pattern = json_object_get_string(val);
|
||||
} else {
|
||||
fprintf(stderr, "Invalid JSON key \"%s\"\n", key);
|
||||
return (0);
|
||||
}
|
||||
break;
|
||||
case json_type_array:
|
||||
ob = json_object_object_get(ob, key);
|
||||
int len = json_object_array_length(ob);
|
||||
json_object *val;
|
||||
for (i = 0; i < len; i++) {
|
||||
val = json_object_array_get_idx(ob, i);
|
||||
count += json_parse(val);
|
||||
}
|
||||
break;
|
||||
case json_type_object:
|
||||
ob = json_object_object_get(ob, key);
|
||||
count += json_parse(ob);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* check and finish if can */
|
||||
if (g_score && g_ban && g_pattern && g_filter) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Adding: %s %s %d %lf\n", s_filter, s_pattern, s_ban, s_score);
|
||||
#endif
|
||||
filter_add(s_filter);
|
||||
pattern_add(s_pattern, s_ban, s_score);
|
||||
count++;
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
if (l_score) {
|
||||
l_score = g_score = false;
|
||||
} else if (l_ban) {
|
||||
l_ban = g_ban = false;
|
||||
} else if (l_pattern) {
|
||||
l_pattern = g_pattern = false;
|
||||
} else if (l_filter) {
|
||||
l_filter = g_filter = false;
|
||||
}
|
||||
|
||||
return (count);
|
||||
}
|
||||
|
||||
static int json_load_file(const char* file)
|
||||
{
|
||||
int i, count = 0;
|
||||
char *json;
|
||||
int fd;
|
||||
struct stat st;
|
||||
fd = open(file, O_RDONLY);
|
||||
fstat(fd, &st);
|
||||
json = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
close(fd);
|
||||
|
||||
struct json_object *obj;
|
||||
|
||||
obj = json_tokener_parse(json);
|
||||
if (json_object_is_type(obj,json_type_array)) {
|
||||
int len = json_object_array_length(obj);
|
||||
json_object *val;
|
||||
for (i = 0; i < len; i++) {
|
||||
val = json_object_array_get_idx(obj, i);
|
||||
count += json_parse(val);
|
||||
}
|
||||
} else if (json_object_is_type(obj, json_type_object)) {
|
||||
count += json_parse(obj);
|
||||
} else {
|
||||
fprintf(stderr, "This does not look like JSON: %s\n", file);
|
||||
return (0);
|
||||
}
|
||||
|
||||
json_object_put(obj);
|
||||
|
||||
munmap(json, st.st_size);
|
||||
return (count);
|
||||
}
|
||||
|
||||
static int json_load_dir(const char *dir)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
DIR *d = opendir(dir);
|
||||
if (!d) {
|
||||
fprintf(stderr, "Skipped reading %s: %s\n", dir, strerror(errno));
|
||||
return (count);
|
||||
}
|
||||
struct dirent *entry;
|
||||
for (;;) {
|
||||
entry = readdir(d);
|
||||
if (!entry)
|
||||
break;
|
||||
size_t l = strlen(entry->d_name);
|
||||
if (l < strlen(".json"))
|
||||
continue;
|
||||
if (strcmp(entry->d_name + l - strlen(".json"), ".json") == 0) {
|
||||
char *p;
|
||||
if (!asprintf(&p, "%s/%s", dir, entry->d_name)) {
|
||||
fprintf(stderr, "asprintf: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* allow /etc/tallow files to override /usr/share/tallow files */
|
||||
if (strcmp(dir, DATADIR "/" PACKAGE_NAME) == 0) {
|
||||
char *sp;
|
||||
if (!asprintf(&sp, SYSCONFDIR "/" PACKAGE_NAME "/%s", entry->d_name)) {
|
||||
fprintf(stderr, "asprintf: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(sp, &st) == 0) {
|
||||
dbg("Skipped " SYSCONFDIR "/" PACKAGE_NAME "/%s\n", entry->d_name);
|
||||
free(sp);
|
||||
continue;
|
||||
}
|
||||
free(sp);
|
||||
}
|
||||
|
||||
int c = json_load_file(p);
|
||||
fprintf(stderr, "%s: %d patterns\n", p, c);
|
||||
count += c;
|
||||
free(p);
|
||||
}
|
||||
|
||||
}
|
||||
closedir(d);
|
||||
|
||||
return (count);
|
||||
}
|
||||
|
||||
void json_load_patterns(void)
|
||||
{
|
||||
int count = 0;
|
||||
count += json_load_dir(DATADIR "/" PACKAGE_NAME);
|
||||
count += json_load_dir(SYSCONFDIR "/" PACKAGE_NAME);
|
||||
fprintf(stderr, "Loaded %d patterns total\n", count);
|
||||
if (count < 1) {
|
||||
/* consider sending a special exit code here */
|
||||
fprintf(stderr, "No patterns loaded, nothing to do!\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* json.h - IP block sshd login abuse
|
||||
*
|
||||
* (C) Copyright 2019 Intel Corporation
|
||||
* Authors:
|
||||
* Auke Kok <auke-jan.h.kok@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; version 3
|
||||
* of the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void json_load_patterns(void);
|
||||
@@ -1,7 +1,7 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "TALLOW" "1" "February 2020" "" ""
|
||||
.TH "TALLOW" "1" "March 2018" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBtallow\fR
|
||||
@@ -31,7 +31,7 @@ The \fBtallow\fR daemon itself has no runtime configuration\. All configuration
|
||||
The \fBUSR1\fR signal causes \fBtallow\fR to print out it\'s internal tracking table of IP addresses\. This requires that tallow is compiled with the \fB\-DDEBUG=1\fR symbol passed to the compiler\.
|
||||
.
|
||||
.SH "SEE ALSO"
|
||||
systemd\-journald(1), iptables(1), ipset(1), tallow\.conf(5), tallow\.patterns(5)
|
||||
systemd\-journald(1), iptables(1), ipset(1), tallow\.conf(5)
|
||||
.
|
||||
.SH "BUGS"
|
||||
\fBtallow\fR is \fBNOT A SECURITY SOLUTION\fR, nor does it protect against random password logins\. A attacker may still be able to logon to your systems if you allow password logins\.
|
||||
@@ -48,7 +48,7 @@ the `-DDEBUG=1` symbol passed to the compiler.
|
||||
|
||||
## SEE ALSO
|
||||
|
||||
systemd-journald(1), iptables(1), ipset(1), tallow.conf(5), tallow.patterns(5)
|
||||
systemd-journald(1), iptables(1), ipset(1), tallow.conf(5)
|
||||
|
||||
## BUGS
|
||||
|
||||
+173
-143
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* tallow.c - IP block sshd login abuse
|
||||
*
|
||||
* (C) Copyright 2015-2019 Intel Corporation
|
||||
* (C) Copyright 2015 Intel Corporation
|
||||
* Authors:
|
||||
* Auke Kok <auke-jan.h.kok@intel.com>
|
||||
*
|
||||
@@ -11,8 +11,6 @@
|
||||
* of the License.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -22,20 +20,59 @@
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include <pcre.h>
|
||||
|
||||
#include <systemd/sd-journal.h>
|
||||
|
||||
#include "json.h"
|
||||
#include "data.h"
|
||||
#ifdef DEBUG
|
||||
#define dbg(args...) fprintf(stderr, ##args)
|
||||
#else
|
||||
#define dbg(args...) do {} while (0)
|
||||
#endif
|
||||
|
||||
struct tallow_struct {
|
||||
char *ip;
|
||||
float score;
|
||||
struct timeval time;
|
||||
struct tallow_struct *next;
|
||||
bool blocked;
|
||||
};
|
||||
|
||||
static struct tallow_struct *head;
|
||||
|
||||
struct whitelist_struct {
|
||||
char *ip;
|
||||
size_t len;
|
||||
struct whitelist_struct *next;
|
||||
};
|
||||
|
||||
static struct whitelist_struct *whitelist;
|
||||
|
||||
#define FILTER_STRING "SYSLOG_IDENTIFIER=sshd"
|
||||
struct pattern_struct {
|
||||
int instant_block;
|
||||
float weight;
|
||||
char *pattern;
|
||||
pcre *re;
|
||||
};
|
||||
|
||||
#define PATTERN_COUNT 10
|
||||
static struct pattern_struct patterns[PATTERN_COUNT] = {
|
||||
{ 0, 0.2, "MESSAGE=Failed .* for .* from ([0-9a-z:.]+) port \\d+ ssh2", NULL},
|
||||
{ 0, 0.2, "MESSAGE=error: PAM: Authentication failure for .* from ([0-9a-z:.]+)", NULL},
|
||||
{10, 0.2, "MESSAGE=Invalid user .* from ([0-9a-z:.]+) port \\d+", NULL},
|
||||
{10, 0.3, "MESSAGE=Did not receive identification string from ([0-9a-z:.]+) port \\d+", NULL},
|
||||
{15, 0.4, "MESSAGE=Bad protocol version identification .* from ([0-9a-z:.]+)", NULL},
|
||||
{15, 0.4, "MESSAGE=Connection closed by authenticating user .* ([0-9a-z:.]+) port \\d+", NULL},
|
||||
{10, 0.3, "MESSAGE=Received disconnect from ([0-9a-z:.]+) port .*\\[preauth\\]", NULL},
|
||||
{10, 0.3, "MESSAGE=Connection closed by ([0-9a-z:.]+) port .*\\[preauth\\]", NULL},
|
||||
{30, 0.5, "MESSAGE=Failed .* for root from ([0-9a-z:.]+) port \\d+ ssh2", NULL},
|
||||
{60, 0.6, "MESSAGE=Unable to negotiate with ([0-9a-z:.]+) port \\d+: no matching key exchange method found.", NULL}
|
||||
};
|
||||
|
||||
#define MAX_OFFSETS 30
|
||||
|
||||
static char ipt_path[PATH_MAX];
|
||||
static char fwcmd_path[PATH_MAX];
|
||||
static int expires = 3600;
|
||||
static int has_ipv6 = 0;
|
||||
static bool nocreate = false;
|
||||
@@ -69,26 +106,6 @@ static void ext_ignore(char *fmt, ...)
|
||||
__attribute__((unused)) int ret = system(cmd);
|
||||
}
|
||||
|
||||
static void reset_rules(void)
|
||||
{
|
||||
/* reset all rules in case the running fw changes */
|
||||
ext_ignore("%s/firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 1 -m set --match-set tallow src -j DROP 2> /dev/null", fwcmd_path);
|
||||
ext_ignore("%s/firewall-cmd --permanent --delete-ipset=tallow 2> /dev/null", fwcmd_path);
|
||||
|
||||
/* delete iptables ref to set before the ipset! */
|
||||
ext_ignore("%s/iptables -t filter -D INPUT -m set --match-set tallow src -j DROP 2> /dev/null", ipt_path);
|
||||
ext_ignore("%s/ipset destroy tallow 2> /dev/null", ipt_path);
|
||||
|
||||
if (has_ipv6) {
|
||||
ext_ignore("%s/firewall-cmd --permanent --direct --remove-rule ipv6 filter INPUT 1 -m set --match-set tallow6 src -j DROP 2> /dev/null", fwcmd_path);
|
||||
ext_ignore("%s/firewall-cmd --permanent --delete-ipset=tallow6 2> /dev/null", fwcmd_path);
|
||||
|
||||
/* delete iptables ref to set before the ipset! */
|
||||
ext_ignore("%s/ip6tables -t filter -D INPUT -m set --match-set tallow6 src -j DROP 2> /dev/null", ipt_path);
|
||||
ext_ignore("%s/ipset destroy tallow6 2> /dev/null", ipt_path);
|
||||
}
|
||||
}
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
static bool done = false;
|
||||
@@ -99,78 +116,38 @@ static void setup(void)
|
||||
if (nocreate)
|
||||
return;
|
||||
|
||||
/* firewalld */
|
||||
char *fwd_path;
|
||||
if (asprintf(&fwd_path, "%s/firewall-cmd", fwcmd_path) < 0) {
|
||||
/* init ipset and iptables */
|
||||
/* delete iptables ref to set before the ipset! */
|
||||
ext_ignore("%s/iptables -t filter -D INPUT -m set --match-set tallow src -j DROP 2> /dev/null", ipt_path);
|
||||
ext_ignore("%s/ipset destroy tallow 2> /dev/null", ipt_path);
|
||||
if (ext("%s/ipset create tallow hash:ip family inet timeout %d", ipt_path, expires)) {
|
||||
fprintf(stderr, "Unable to create ipv4 ipset.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (ext("%s/iptables -t filter -A INPUT -m set --match-set tallow src -j DROP", ipt_path)) {
|
||||
fprintf(stderr, "Unable to create iptables rule.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if ((access(fwd_path, X_OK) == 0) && ext("%s/firewall-cmd --state --quiet", fwcmd_path) == 0) {
|
||||
fprintf(stdout, "firewalld is running and will be used by tallow.\n");
|
||||
|
||||
reset_rules();
|
||||
|
||||
/* create ipv4 rule and ipset */
|
||||
if (ext("%s/firewall-cmd --permanent --quiet --new-ipset=tallow --type=hash:ip --family=inet --option=timeout=%d", fwcmd_path, expires)) {
|
||||
fprintf(stderr, "Unable to create ipv4 ipset with firewall-cmd.\n");
|
||||
if (has_ipv6) {
|
||||
ext_ignore("%s/ip6tables -t filter -D INPUT -m set --match-set tallow6 src -j DROP 2> /dev/null", ipt_path);
|
||||
ext_ignore("%s/ipset destroy tallow6 2> /dev/null", ipt_path);
|
||||
if (ext("%s/ipset create tallow6 hash:ip family inet6 timeout %d", ipt_path, expires)) {
|
||||
fprintf(stderr, "Unable to create ipv6 ipset.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (ext("%s/firewall-cmd --permanent --direct --quiet --add-rule ipv4 filter INPUT 1 -m set --match-set tallow src -j DROP", fwcmd_path)) {
|
||||
fprintf(stderr, "Unable to create ipv4 firewalld rule.\n");
|
||||
if (ext("%s/ip6tables -t filter -A INPUT -m set --match-set tallow6 src -j DROP", ipt_path)) {
|
||||
fprintf(stderr, "Unable to create ipt6ables rule.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* create ipv6 rule and ipset */
|
||||
if (has_ipv6) {
|
||||
if (ext("%s/firewall-cmd --permanent --quiet --new-ipset=tallow6 --type=hash:ip --family=inet6 --option=timeout=%d", fwcmd_path, expires)) {
|
||||
fprintf(stderr, "Unable to create ipv6 ipset with firewall-cmd.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (ext("%s/firewall-cmd --permanent --direct --quiet --add-rule ipv6 filter INPUT 1 -m set --match-set tallow6 src -j DROP ", fwcmd_path)) {
|
||||
fprintf(stderr, "Unable to create ipv6 firewalld rule.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* reload firewalld for ipsets to load */
|
||||
if (ext("%s/firewall-cmd --reload --quiet", fwcmd_path, expires)) {
|
||||
fprintf(stderr, "Unable to reload firewalld rules.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
/* iptables */
|
||||
reset_rules();
|
||||
|
||||
/* create ipv4 rule and ipset */
|
||||
if (ext("%s/ipset create tallow hash:ip family inet timeout %d", ipt_path, expires)) {
|
||||
fprintf(stderr, "Unable to create ipv4 ipset.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (ext("%s/iptables -t filter -A INPUT -m set --match-set tallow src -j DROP", ipt_path)) {
|
||||
fprintf(stderr, "Unable to create iptables rule.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* create ipv6 rule and ipset */
|
||||
if (has_ipv6) {
|
||||
if (ext("%s/ipset create tallow6 hash:ip family inet6 timeout %d", ipt_path, expires)) {
|
||||
fprintf(stderr, "Unable to create ipv6 ipset.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (ext("%s/ip6tables -t filter -A INPUT -m set --match-set tallow6 src -j DROP", ipt_path)) {
|
||||
fprintf(stderr, "Unable to create ipt6ables rule.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(fwd_path);
|
||||
}
|
||||
|
||||
static void block(struct block_struct *s, int instant_block)
|
||||
static void block(struct tallow_struct *s, int instant_block)
|
||||
{
|
||||
setup();
|
||||
|
||||
|
||||
if (strchr(s->ip, ':')) {
|
||||
if (has_ipv6) {
|
||||
if (instant_block > 0) {
|
||||
@@ -198,10 +175,38 @@ static void block(struct block_struct *s, int instant_block)
|
||||
}
|
||||
}
|
||||
|
||||
void find(const char *ip, float weight, int instant_block)
|
||||
static void whitelist_add(char *ip)
|
||||
{
|
||||
struct block_struct *s = blocks;
|
||||
struct block_struct *n;
|
||||
struct whitelist_struct *w = whitelist;
|
||||
struct whitelist_struct *n;
|
||||
|
||||
while (w && w->next)
|
||||
w = w->next;
|
||||
|
||||
n = calloc(1, sizeof(struct whitelist_struct));
|
||||
if (!n) {
|
||||
fprintf(stderr, "Out of memory.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
n->ip = strdup(ip);
|
||||
size_t l = strlen(ip);
|
||||
if ((ip[l-1] == '.') || (ip[l-1] == ':'))
|
||||
n->len = l;
|
||||
else
|
||||
n->len = -1;
|
||||
|
||||
if (!whitelist)
|
||||
whitelist = n;
|
||||
else
|
||||
w->next = n;
|
||||
}
|
||||
|
||||
static void find(const char *ip, float weight, int instant_block)
|
||||
{
|
||||
struct tallow_struct *s = head;
|
||||
struct tallow_struct *n;
|
||||
struct whitelist_struct *w = whitelist;
|
||||
|
||||
if (!ip)
|
||||
return;
|
||||
@@ -214,8 +219,17 @@ void find(const char *ip, float weight, int instant_block)
|
||||
if (strspn(ip, "0123456789abcdef:.") != strlen(ip))
|
||||
return;
|
||||
|
||||
if (whitelist_find(ip))
|
||||
return;
|
||||
/* whitelist */
|
||||
while (w) {
|
||||
if (w->len > 0) {
|
||||
if (!strncmp(w->ip, ip, w->len))
|
||||
return;
|
||||
} else {
|
||||
if (!strcmp(w->ip, ip))
|
||||
return;
|
||||
}
|
||||
w = w->next;
|
||||
}
|
||||
|
||||
/* walk and update entry */
|
||||
while (s) {
|
||||
@@ -244,14 +258,14 @@ void find(const char *ip, float weight, int instant_block)
|
||||
}
|
||||
|
||||
/* append */
|
||||
n = calloc(1, sizeof(struct block_struct));
|
||||
n = calloc(1, sizeof(struct tallow_struct));
|
||||
if (!n) {
|
||||
fprintf(stderr, "Out of memory.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!blocks)
|
||||
blocks = n;
|
||||
if (!head)
|
||||
head = n;
|
||||
else
|
||||
s->next = n;
|
||||
|
||||
@@ -274,7 +288,7 @@ void find(const char *ip, float weight, int instant_block)
|
||||
static void sigusr1(int u __attribute__ ((unused)))
|
||||
{
|
||||
fprintf(stderr, "Dumping score list on request:\n");
|
||||
struct block_struct *s = blocks;
|
||||
struct tallow_struct *s = head;
|
||||
while (s) {
|
||||
fprintf(stderr, "%ld %s %1.3f\n", s->time.tv_sec, s->ip, s->score);
|
||||
s = s->next;
|
||||
@@ -282,22 +296,54 @@ static void sigusr1(int u __attribute__ ((unused)))
|
||||
}
|
||||
#endif
|
||||
|
||||
static void prune(void)
|
||||
{
|
||||
struct tallow_struct *s = head;
|
||||
struct tallow_struct *p;
|
||||
struct timeval tv;
|
||||
|
||||
(void) gettimeofday(&tv, NULL);
|
||||
p = NULL;
|
||||
|
||||
while (s) {
|
||||
/*
|
||||
* Expire all records, but if they are blocked, make sure to
|
||||
* expire them *before* the ipset rule expires, otherwise
|
||||
* you might get an IP to bypass checks.
|
||||
*/
|
||||
time_t age = tv.tv_sec - s->time.tv_sec;
|
||||
if ((age > expires) ||
|
||||
((s->blocked) && (age > expires / 2))) {
|
||||
dbg("Expired record for %s\n", s->ip);
|
||||
if (p) {
|
||||
p->next = s->next;
|
||||
free(s->ip);
|
||||
free(s);
|
||||
s = p->next;
|
||||
continue;
|
||||
} else {
|
||||
head = s->next;
|
||||
free(s->ip);
|
||||
free(s);
|
||||
s = head;
|
||||
p = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
p = s;
|
||||
s = s->next;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int r;
|
||||
FILE *f;
|
||||
int timeout = 60;
|
||||
long long unsigned int last_timestamp = 0;
|
||||
|
||||
json_load_patterns();
|
||||
|
||||
strcpy(ipt_path, "/usr/sbin");
|
||||
strcpy(fwcmd_path, "/usr/sbin");
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Debug output enabled. Send SIGUSR1 to dump internal state table\n");
|
||||
|
||||
struct sigaction s;
|
||||
|
||||
memset(&s, 0, sizeof(struct sigaction));
|
||||
@@ -308,7 +354,7 @@ int main(void)
|
||||
if (access("/proc/sys/net/ipv6", R_OK | X_OK) == 0)
|
||||
has_ipv6 = 1;
|
||||
|
||||
f = fopen(SYSCONFDIR "/tallow.conf", "r");
|
||||
f = fopen("/etc/tallow.conf", "r");
|
||||
if (f) {
|
||||
char buf[256];
|
||||
char *key;
|
||||
@@ -333,8 +379,6 @@ int main(void)
|
||||
// todo: filter leading/trailing whitespace
|
||||
if (!strcmp(key, "ipt_path"))
|
||||
strncpy(ipt_path, val, PATH_MAX - 1);
|
||||
if (!strcmp(key, "fwcmd_path"))
|
||||
strncpy(fwcmd_path, val, PATH_MAX - 1);
|
||||
if (!strcmp(key, "expires"))
|
||||
expires = atoi(val);
|
||||
if (!strcmp(key, "whitelist"))
|
||||
@@ -350,11 +394,8 @@ int main(void)
|
||||
if (!has_ipv6)
|
||||
fprintf(stdout, "ipv6 support disabled.\n");
|
||||
|
||||
if (!whitelist) {
|
||||
if (!whitelist)
|
||||
whitelist_add("127.0.0.1");
|
||||
whitelist_add("192.168.");
|
||||
whitelist_add("10.");
|
||||
}
|
||||
|
||||
r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
|
||||
if (r < 0) {
|
||||
@@ -362,14 +403,9 @@ int main(void)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* add all filters */
|
||||
struct filter_struct *flt = filters;
|
||||
while (flt) {
|
||||
sd_journal_add_match(j, flt->filter, 0);
|
||||
flt = flt->next;
|
||||
}
|
||||
|
||||
/* go to the tail and wait */
|
||||
/* ffwd journal */
|
||||
sd_journal_add_match(j, FILTER_STRING, 0);
|
||||
r = sd_journal_seek_tail(j);
|
||||
sd_journal_wait(j, (uint64_t) 0);
|
||||
dbg("sd_journal_seek_tail() returned %d\n", r);
|
||||
@@ -377,11 +413,22 @@ int main(void)
|
||||
r++;
|
||||
dbg("Forwarded through %d items in the journal to reach the end\n", r);
|
||||
|
||||
fprintf(stderr, PACKAGE_STRING " Started\n");
|
||||
fprintf(stderr, "Started\n");
|
||||
|
||||
for (int i = 0; i < PATTERN_COUNT; i++) {
|
||||
int err;
|
||||
const char *pcre_err;
|
||||
patterns[i].re = pcre_compile(patterns[i].pattern, 0, &pcre_err, &err, NULL);
|
||||
if (!patterns[i].re) {
|
||||
fprintf(stderr, "PCRE compilation failed. Pattern %d, offset %d: %s\n",
|
||||
i, err, pcre_err);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
const void *d, *dt;
|
||||
size_t l, dl;
|
||||
const void *d;
|
||||
size_t l;
|
||||
|
||||
r = sd_journal_wait(j, (uint64_t) timeout * 1000000);
|
||||
if (r == SD_JOURNAL_INVALIDATE) {
|
||||
@@ -395,20 +442,6 @@ int main(void)
|
||||
while (sd_journal_next(j) != 0) {
|
||||
char *m;
|
||||
|
||||
/*
|
||||
* discard messages older than ones we've already seen before
|
||||
* this happens when the journal rotates - we get replayed events
|
||||
*/
|
||||
if (sd_journal_get_data(j, "_SOURCE_REALTIME_TIMESTAMP", &dt, &dl) == 0) {
|
||||
long long unsigned int lt = atoll(dt + strlen("_SOURCE_REALTIME_TIMESTAMP="));
|
||||
if (lt > last_timestamp)
|
||||
last_timestamp = lt;
|
||||
else if (lt < last_timestamp) {
|
||||
dbg("Discarding old entry: %llu - %llu\n", lt, last_timestamp);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (sd_journal_get_data(j, "MESSAGE", &d, &l) < 0) {
|
||||
fprintf(stderr, "Failed to read message field: %s\n", strerror(-r));
|
||||
break;
|
||||
@@ -417,28 +450,25 @@ int main(void)
|
||||
m = strndup(d, l+1);
|
||||
m[l] = '\0';
|
||||
|
||||
struct pattern_struct *pat = patterns;
|
||||
while (pat) {
|
||||
for (int i = 0; i < PATTERN_COUNT; i++) {
|
||||
int off[MAX_OFFSETS];
|
||||
int ret = pcre_exec(pat->re, NULL, m, l, 0, 0, off, MAX_OFFSETS);
|
||||
int ret = pcre_exec(patterns[i].re, NULL, m, l, 0, 0, off, MAX_OFFSETS);
|
||||
if (ret == 2) {
|
||||
const char *s;
|
||||
ret = pcre_get_substring(m, off, 2, 1, &s);
|
||||
if (ret > 0) {
|
||||
dbg("%s == %s\n", s, pat->pattern);
|
||||
find(s, pat->weight, pat->instant_block);
|
||||
dbg("%s == %s\n", s, patterns[i].pattern);
|
||||
find(s, patterns[i].weight, patterns[i].instant_block);
|
||||
pcre_free_substring(s);
|
||||
}
|
||||
}
|
||||
|
||||
pat = pat->next;
|
||||
}
|
||||
|
||||
free(m);
|
||||
|
||||
}
|
||||
|
||||
prune(expires);
|
||||
prune();
|
||||
}
|
||||
|
||||
sd_journal_close(j);
|
||||
@@ -1,10 +1,7 @@
|
||||
|
||||
# tallow.conf - see `man tallow.conf` for more information
|
||||
|
||||
#fwcmd_path=/usr/sbin
|
||||
#ipt_path=/usr/sbin
|
||||
#expires=3600
|
||||
#whitelist=127.0.0.1
|
||||
#whitelist=192.168.
|
||||
#whitelist=10.
|
||||
#ipv6=0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "TALLOW" "5" "February 2020" "" ""
|
||||
.TH "TALLOW" "5" "January 2018" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBtallow\fR
|
||||
@@ -19,62 +19,39 @@ tallow\.conf \- Tallow daemon configuration file
|
||||
This file is read on startup by the tallow(1) daemon, and can be used to provide options to the tallow daemon\. If not present, tallow will operate with built\-in defaults\.
|
||||
.
|
||||
.SH "OPTIONS"
|
||||
\fBfwcmd_path\fR=\fB<string>\fR Specifies the location of the ipset(1) firewall\-cmd(1) programs\. By default, tallow will look in "/usr/sbin" for them\.
|
||||
.
|
||||
.P
|
||||
\fBipt_path\fR=\fB<string>\fR Specifies the location of the ipset(1) program and iptables(1) or ip6tables(1) programs\. By default, tallow will look in "/usr/sbin" for them\.
|
||||
\fBipt_path\fR=\fB<string>\fR Specifies the location of the ipset(1), iptables(1) or ip6tables(1) program\. By default, tallow will look in "/usr/sbin" for them\.
|
||||
.
|
||||
.P
|
||||
\fBexpires\fR=\fB<int>\fR The number of seconds that IP addresses are blocked for\. Note that due to the implementation, IP addresses may be blocked for much longer than this period\. If IP addresses are seen, but not blocked within this period, they are also removed from the watch list\. Defaults to 3600s\.
|
||||
.
|
||||
.P
|
||||
\fBwhitelist\fR=\fB<ip address|pattern>\fR Specify an IP address or \fBpattern\fR that should never be blocked\. Multiple IP addresses can be included by repeating the \fBwhitelist\fR option several times\. By default, 127\.0\.0\.1, 192\.168\., and 10\. are whitelisted\. If you create a manual whitelist, you must include these entries if you want to continue them to be whitelisted as well, otherwise they will be omitted from the whitelist\.
|
||||
\fBwhitelist\fR=\fB<ip address|pattern>\fR Specify an IP address or \fBpattern\fR that should never be blocked\. Multiple IP addresses can be included by repeating the \fBwhitelist\fR option several times\. By default, only 127\.0\.0\.1 is whitelisted\.
|
||||
.
|
||||
.P
|
||||
If the last character of the listed ip adress is a \fB\.\fR or a \fB:\fR, then the matching is only performed on the leftmost characters of an IP address against the whitelist entry\. For instance, if you whitelist \fB10\.\fR then all IP addresses in the \fB10/8\fR subnet mask will match this whitelist entry and never be blocked\.
|
||||
.
|
||||
.P
|
||||
\fBipv6\fR=\fB<0|1>\fR Enable or disable ipv6 (ip6tables) support\. Ipv6 is disabled automatically on systems that do not appear to have ipv6 support and enabled when ipv6 is present\. Use this option to explicitly disable ipv6 support if your system does not have ipv6 or is missing ip6tables\. Even with ipv6 disabled, tallow will track and log ipv6 addresses\.
|
||||
\fBipv6\fR=\fB<0|1>\fR Enable of disable ipv6 (ip6tables) support\. Ipv6 is disabled automatically on systems that do not appear to have ipv6 support and enabled when ipv6 is present\. Use this option to explicitly disable ipv6 support if your system does not have ipv6 or is missing ip6tables\. Even with ipv6 disabled, tallow will track and log ipv6 addresses\.
|
||||
.
|
||||
.P
|
||||
\fBnocreate\fR=\fB<0|1>\fR Disable the creation of firewall rules and ipset sets\. By default, tallow will create new firewall\-cmd(1) or iptables(1) and ip6tables(1) rules when needed automatically\. If set to \fB1\fR, \fBtallow(1)\fR will not create any new firewall DROP rules or ipset sets that are needed work\. You should create them manually before tallow starts up and remove them afterwards using the sets of commands below\.
|
||||
.
|
||||
.P
|
||||
Use the following commands if you\'re using iptables(1):
|
||||
\fBnocreate\fR=\fB<0|1>\fR Disable the creation of iptables rules and ipset sets\. By default, tallow will create new iptables(1) and ip6tables(1) rules when needed automatically\. If set to \fB1\fR, \fBtallow(1)\fR will not create any new iptables rules or ipset sets to work\. You should create them manually before tallow starts up and remove them afterwards\. To create them manually, you can use the following commands:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
iptables \-t filter \-I INPUT \-m set \-\-match\-set tallow src \-j DROP
|
||||
ipset create tallow hash:ip family inet timeout 3600
|
||||
iptables \-t filter \-I INPUT 1 \-m set \-\-match\-set tallow src \-j DROP
|
||||
|
||||
ip6tables \-t filter \-I INPUT \-m set \-\-match\-set tallow6 src \-j DROP
|
||||
ipset create tallow6 hash:ip family inet6 timeout 3600
|
||||
ip6tables \-t filter \-I INPUT 1 \-m set \-\-match\-set tallow6 src \-j DROP
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Use the following commands if you\'re using firewalld(1):
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
firewall\-cmd \-\-permanent \-\-new\-ipset=tallow \-\-type=hash:ip \-\-family=inet \-\-option=timeout=3600
|
||||
firewall\-cmd \-\-permanent \-\-direct \-\-add\-rule ipv4 filter INPUT 1 \-m set \-\-match\-set tallow src \-j DROP
|
||||
|
||||
firewall\-cmd \-\-permanent \-\-new\-ipset=tallow6 \-\-type=hash:ip \-\-family=inet6 \-\-option=timeout=3600
|
||||
firewall\-cmd \-\-permanent \-\-direct \-\-add\-rule ipv6 filter INPUT 1 \-m set \-\-match\-set tallow6 src \-j DROP
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "SEE ALSO"
|
||||
tallow(1), tallow\.patterns(5)
|
||||
tallow(1)
|
||||
.
|
||||
.SH "AUTHOR"
|
||||
Auke Kok \fIauke\-jan\.h\.kok@intel\.com\fR
|
||||
@@ -0,0 +1,76 @@
|
||||
|
||||
## tallow.conf
|
||||
|
||||
The tallow configuration file
|
||||
|
||||
## NAME
|
||||
|
||||
tallow.conf - Tallow daemon configuration file
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`/etc/tallow.conf`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
This file is read on startup by the tallow(1) daemon, and can
|
||||
be used to provide options to the tallow daemon. If not present,
|
||||
tallow will operate with built-in defaults.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
`ipt_path`=`<string>`
|
||||
Specifies the location of the ipset(1), iptables(1) or ip6tables(1)
|
||||
program. By default, tallow will look in "/usr/sbin" for them.
|
||||
|
||||
`expires`=`<int>`
|
||||
The number of seconds that IP addresses are blocked for. Note that
|
||||
due to the implementation, IP addresses may be blocked for much
|
||||
longer than this period. If IP addresses are seen, but not
|
||||
blocked within this period, they are also removed from the
|
||||
watch list. Defaults to 3600s.
|
||||
|
||||
`whitelist`=`<ip address|pattern>`
|
||||
Specify an IP address or `pattern` that should never be
|
||||
blocked. Multiple IP addresses can be included by repeating the
|
||||
`whitelist` option several times. By default, only 127.0.0.1 is
|
||||
whitelisted.
|
||||
|
||||
If the last character of the listed ip adress is a `.` or a `:`, then
|
||||
the matching is only performed on the leftmost characters of an IP
|
||||
address against the whitelist entry. For instance, if you whitelist
|
||||
`10.` then all IP addresses in the `10/8` subnet mask will match this
|
||||
whitelist entry and never be blocked.
|
||||
|
||||
`ipv6`=`<0|1>`
|
||||
Enable of disable ipv6 (ip6tables) support. Ipv6 is disabled
|
||||
automatically on systems that do not appear to have ipv6 support
|
||||
and enabled when ipv6 is present. Use this option to explicitly
|
||||
disable ipv6 support if your system does not have ipv6 or is
|
||||
missing ip6tables. Even with ipv6 disabled, tallow will track
|
||||
and log ipv6 addresses.
|
||||
|
||||
`nocreate`=`<0|1>`
|
||||
Disable the creation of iptables rules and ipset sets. By default,
|
||||
tallow will create new iptables(1) and ip6tables(1) rules when needed
|
||||
automatically. If set to `1`, `tallow(1)` will not create any new
|
||||
iptables rules or ipset sets to work. You should create them manually
|
||||
before tallow starts up and remove them afterwards. To create them
|
||||
manually, you can use the following commands:
|
||||
|
||||
```
|
||||
iptables -t filter -I INPUT -m set --match-set tallow src -j DROP
|
||||
ipset create tallow hash:ip family inet timeout 3600
|
||||
|
||||
ip6tables -t filter -I INPUT -m set --match-set tallow6 src -j DROP
|
||||
ipset create tallow6 hash:ip family inet6 timeout 3600
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
|
||||
tallow(1)
|
||||
|
||||
## AUTHOR
|
||||
|
||||
Auke Kok <auke-jan.h.kok@intel.com>
|
||||
|
||||
@@ -6,7 +6,6 @@ Description=Tallow Service
|
||||
ExecStart=@prefix@/sbin/tallow
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
Nice=10
|
||||
|
||||
[Install]
|
||||
WantedBy=network.target
|
||||
Reference in New Issue
Block a user