33 Commits
v1 .. v6

Author SHA1 Message Date
Auke Kok ff2a47756f v6 2017-12-14 14:52:35 -08:00
Auke Kok b18f636489 Assure blocked IP's expire before they can be detected again.
Otherwise, in rare conditions, an IP address may appear just
before it would expire, which would cause the IP to not be
blocked again.
2017-12-14 14:31:41 -08:00
Auke Kok 6e0251d3dc Update manual pages to indicate the various recent changes. 2017-12-14 10:00:44 -08:00
Auke Kok 60a90adbc5 Instant throttling of confirmed abuse.
Automatically `block` certain clients based on severity of the
produced error messages. These clients are for sure doing something
bad, and we don't want to let them try this more times before dropping
their packets.

The block is issued immediately, but it only lasts a short time.
Most likely, additional messages will come in after that cause a
longer ban anyway.

This also forces overwriting of ipset entries without warning, which
helps to keep the ipset list in sync without further statekeeping.

The pattern list has been expanded with the instant_block integer
value, which indicates that if the pattern matches, the IP should be
dropped for how many seconds.
2017-12-14 09:57:52 -08:00
Auke Kok 1f43bcbf12 Debug code for pruning. 2017-12-13 15:02:13 -08:00
Auke Kok 8da71a2184 Remove multiple block spam.
Some minor cleanups in here, and 2 extra rules. This now prevents
multiple messages coming in and causing 2 block commands to be
issued.
2017-12-12 15:26:30 -08:00
Auke Kok 38b09c3b07 Re-add debug printout of state, move various prints to debug build.
Compiling with -DDEBUG=1 will now create an extra verbose version
that can be used to debug the pattern matching in more detail.

The non-debug build is now less verbose, as a result.

Send a USR1 signal to the process to make it dump the current
state table.
2017-12-11 16:47:59 -08:00
Auke Kok 20f4c970de Add 6 more relevant SSHD patterns.
Some of these come with a higher weight, as they're very obvious
points of abuse/probing, like attempting to use old protocols or not
being able to use modern key types.
2017-12-11 16:47:32 -08:00
Auke Kok 32c20f190f Allow multiple patterns.
We do not want to rely solely on one pattern for detecting login
attempts. This change creates a simple static list with patterns that
have a weight. If the pattern matches, the weight is added to the IP
score total. If the score total exceeds the max, the IP is blocked.

Previously we blocked on count=3, now we block when score=1.0.
The weight from the standard invalid user login is now dropped to
0.4 to have the same effect.

The `threshold` parameter is now therefore obsolete, and if found in
the config file, it will be ignored.
2017-12-11 14:04:14 -08:00
Gwenn Gueguen e3e4388654 Replace path_iptables with ipt_path in sample tallow.conf 2017-05-22 13:46:27 -07:00
Auke Kok 8a05303802 v5 2017-05-17 17:18:01 -07:00
Auke Kok e296f501c7 Use pcre to match logs, and find IP addresses.
This is a much more reliable method to extract the IP address
from the log entries, and allows us to consolidate 2 matches into
a single operation.

Once matched, we extract the IP substring and pass it to `find()`
as usual. We can add more regexes later if that is useful.
2017-05-16 15:55:18 -07:00
Auke Kok 379f74a071 Fix Travis.
Check in the man pages so we don't need to run `ronn` on CI.
2017-05-11 11:24:20 -07:00
Auke Kok 9042a01eab v4 2017-05-11 08:56:56 -07:00
Auke Kok 2225ee029d Revert "also catch port probers that try ssl level evils"
This reverts commit dc8f37e41f.

This message can print on a normal and legitimate user when they
disconnect, and therefore would be a false positive. We should
100% never get close to blocking legitimate users, ever.
2017-05-10 21:49:16 -07:00
Auke Kok dee23b8275 Lazy initialization.
At start, only initialize the journal, but wait until we actually
need to block anything before initializing ipset and iptables.
2017-05-10 21:14:07 -07:00
Auke Kok 34bd8d55bd Remove SIGUSR1 handler - dumping lists is obsolete with ipset. 2017-05-10 21:07:18 -07:00
Auke Kok 2a33768293 Don't break our LL on block.
We will prune regularly anyway, so this is entirely unneeded.
2017-05-10 20:59:36 -07:00
Auke Kok ea958fd2b5 v3 2017-05-08 08:49:15 -07:00
Auke Kok 4547892d56 Attempt to build against old systemd versions as well.
In case libsystemd isn't found, try libsystemd-journal as well.
2017-05-07 21:09:58 -07:00
Auke Kok c661a20e33 Revert removal of prune().
We can't just delete an entry only when it is blocked, this
would forever leave all entries lingering in the list until
they hit the limit, and it would likely consume lots of memory.

Instead, we'll prune only based on timestamp values. This removes
old entries automatically regularly, but leaves new hits that
haven't hit the expiry time. If IPs get blocked, they're not
removed, but the expiry time will remove them. This will
assure that hosts that try in large intervals actually get
blocked again right away.
2017-05-07 20:36:32 -07:00
Arjan van de Ven 9f37520c72 ip can be NULL (output of strtok) 2017-05-07 20:23:37 -07:00
Arjan van de Ven dc8f37e41f also catch port probers that try ssl level evils 2017-05-07 20:23:31 -07:00
Auke Kok d590c8f67f v2: ipset release. 2017-05-07 00:17:48 -07:00
Auke Kok ec2b5cbfc0 Make ipset init clean and working. 2017-05-07 00:05:25 -07:00
Auke Kok cb41c16e93 Minor ipset fixes. 2017-05-06 23:38:28 -07:00
Auke Kok 40568eb4cd Man pages and checked out folders. 2017-05-06 23:14:06 -07:00
Auke Kok 992927798d Convert to ipset.
Create `tallow` and `tallow6` ipsets, hook up to iptables
and create a single rule in the INPUT chain of the filter
table.

The ipsets created have `expire` timeouts set by default
which removes the need to do pruning, so we can erase entries
immediately from our LL when blocking.
2017-05-06 23:12:22 -07:00
Arjan van de Ven fba8921952 add to .gitignore 2017-05-06 22:36:55 -07:00
Arjan van de Ven 73e9cd7011 add travis support 2017-05-06 22:36:55 -07:00
Arjan van de Ven a4d9d9688e add -W 2017-05-06 22:35:49 -07:00
Arjan van de Ven 35eeabb146 avoid a large .data section by just initializing the big structures at run time 2017-05-06 22:35:49 -07:00
Auke Kok 08d45d39fd Convert man page to ronn generated .md input format. 2017-05-06 22:12:30 -07:00
10 changed files with 401 additions and 193 deletions
+3 -1
View File
@@ -11,6 +11,8 @@ install-sh
missing
tallow
tallow-*.tar.gz
tallow-1/
tallow-*/
tallow.o
tallow.service
*~
DEADJOE
+21
View File
@@ -0,0 +1,21 @@
sudo: required
dist: trusty
language: c
compiler: gcc
os: linux
before_script:
- ./autogen.sh
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- libsystemd-journal-dev
- valgrind
- autoconf
- automake
script:
- ./configure && make && make distcheck
+13 -4
View File
@@ -1,17 +1,26 @@
AM_CFLAGS = -g $(LIBSYSTEMD_CFLAGS) -Wall -Wno-uninitialized
AM_CFLAGS = -g $(PCRE_CFLAGS) $(LIBSYSTEMD_CFLAGS) -Wall -Wno-uninitialized -W -D_FORTIFY_SOURCE=2
systemdsystemunitdir = @SYSTEMD_SYSTEMUNITDIR@
systemdsystemunit_DATA = tallow.service
sbin_PROGRAMS = tallow
tallow_SOURCES = tallow.c
tallow_LDADD = $(LIBSYSTEMD_LIBS)
tallow_LDADD = $(PCRE_LIBS) $(LIBSYSTEMD_LIBS)
EXTRA_DIST = AUTHORS COPYING INSTALL tallow.service.in
EXTRA_DIST = AUTHORS COPYING INSTALL tallow.service.in tallow.1.md
dist_man_MANS = tallow.1 tallow.conf.5
dist_doc_DATA = tallow.conf
dist_man_MANS = tallow.1 tallow.conf.5
DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$(DESTDIR)$(SYSTEMDSYSTEMUNITDIR)
docs: tallow.1 tallow.conf.5
tallow.conf.5:
ronn -r tallow.conf.5.md --pipe > tallow.conf.5
tallow.1:
ronn -r tallow.1.md --pipe > tallow.1
+5 -2
View File
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.64])
AC_INIT([tallow], [1], [auke-jan.h.kok@intel.com])
AC_INIT([tallow], [6], [auke-jan.h.kok@intel.com])
AM_INIT_AUTOMAKE([])
AC_CONFIG_FILES([Makefile])
@@ -10,9 +10,12 @@ AC_CONFIG_FILES([Makefile])
AC_PROG_CC
AC_PROG_INSTALL
PKG_CHECK_MODULES([LIBSYSTEMD], [libsystemd])
PKG_CHECK_MODULES(PCRE, libpcre)
PKG_CHECK_MODULES(LIBSYSTEMD, libsystemd,, [PKG_CHECK_MODULES(LIBSYSTEMD, libsystemd-journal)])
AC_SUBST(LIBSYSTEMD_CFLAGS)
AC_SUBST(LIBSYSTEMD_LIBS)
AC_SUBST(LIBSYSTEMD_JOURNAL_CFLAGS)
AC_SUBST(LIBSYSTEMD_JOURNAL_LIBS)
AC_ARG_WITH([systemdsystemunitdir], AC_HELP_STRING([--with-systemdsystemunitdir=DIR],
[path to systemd system service directory]), [path_systemdsystemunit=${withval}],
+37 -41
View File
@@ -1,41 +1,37 @@
.TH tallow 1 "31 October 2012" ".1" "Tallow"
.SH NAME
Tallow \- Reduce log clutter due to ssh login attempts.
.SH SYNOPSIS
/usr/sbin/tallow
.SH DESCRIPTION
\fBtallow\fR is a daemon that watches the systemd journal for
messages from the \fBsshd\fR service. It parses the messages
and looks for attempted random logins such as failed logins to the
root account and failed logins to invalid user accounts.
.PP
If such logins were detected, the offending IP address is stored
in a list. Items from this list are regularly purged, but if
the amount of times that a specific IP address is seen exceeds
a threshold (default 3), an iptables(1) rule is inserted in the
\fBTALLOW\fR chain in the \fBfilter\fR netfilter table. The
rule will match all packets from the IP address and \fBDROP\dR
them.
.PP
The system administrator needs to assure that all incoming packets
are routed through the \fBTALLOW\fR chain by inserting a rule
appropriately, e.g. \`iptables -I INPUT -j TALLOW\`. The \fBTALLOW\fR
chain may have to be created manually first with e.g. \`iptables -N
TALLOW\`.
.PP
Care should be taken to assure that legitimate users are not
blocked inadvertently. You may wish to list any valid IP address
with the whitelist option in tallow.conf(5). Multiple addresses
can be whitelisted.
.SH OPTIONS
The \fBtallow\fR daemon itself has no runtime configuration. All
configuration is done through the tallow.conf(5) config file.
.SH SEE ALSO
systemd-journald(1), iptables(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.
.SH AUTHOR
Auke Kok <auke-jan.h.kok@intel.com>
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "TALLOW" "1" "December 2017" "" ""
.
.SH "NAME"
\fBtallow\fR
.
.SH "tallow"
Reduce log clutter due to ssh login attempts\.
.
.SH "SYNOPSIS"
\fB/usr/sbin/tallow\fR
.
.SH "DESCRIPTION"
\fBtallow\fR is a daemon that watches the systemd journal for messages from the \fBsshd\fR service\. It parses the messages and looks for attempted random logins such as failed logins to the root account and failed logins to invalid user accounts, and various other obviously malicious login attempts that try things as forcing old protocols, or weak key systems\.
.
.P
If such logins were detected, the offending IP address is stored in a list\. Items from this list are regularly purged, but if the amount of times that a specific IP address is seen exceeds a threshold, an ipset(1) entry is inserted in the \fBtallow\fR or \fBtallow6\fR ipset, and further packets from that ip address will be blocked by an \fBiptables(1)\fR or \fBip6tables(1)\fR rule that tallow creates at startup\. Additionally, certain types of login failure will trigger a short term ban of further packets from the offending IP address immediately\.
.
.P
The system administrator needs to assure that the tallow and tallow6 ipsets are left alone and that the inserted iptables rules are properly matching on packets\.
.
.P
Care should be taken to assure that legitimate users are not blocked inadvertently\. You may wish to list any valid IP address with the whitelist option in tallow\.conf(5)\. Multiple addresses can be whitelisted\.
.
.SH "OPTIONS"
The \fBtallow\fR daemon itself has no runtime configuration\. All configuration is done through the tallow\.conf(5) config file\.
.
.SH "SEE ALSO"
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\.
.
.SH "AUTHOR"
Auke Kok \fIauke\-jan\.h\.kok@intel\.com\fR
+55
View File
@@ -0,0 +1,55 @@
## tallow
Reduce log clutter due to ssh login attempts.
## SYNOPSIS
`/usr/sbin/tallow`
## DESCRIPTION
`tallow` is a daemon that watches the systemd journal for messages
from the `sshd` service. It parses the messages and looks for
attempted random logins such as failed logins to the root account and
failed logins to invalid user accounts, and various other obviously
malicious login attempts that try things as forcing old protocols,
or weak key systems.
If such logins were detected, the offending IP address is stored in
a list. Items from this list are regularly purged, but if the amount
of times that a specific IP address is seen exceeds a threshold,
an ipset(1) entry is inserted in the `tallow` or `tallow6`
ipset, and further packets from that ip address will be blocked
by an `iptables(1)` or `ip6tables(1)` rule that tallow creates at
startup. Additionally, certain types of login failure will trigger
a short term ban of further packets from the offending IP address
immediately.
The system administrator needs to assure that the tallow and tallow6
ipsets are left alone and that the inserted iptables rules are properly
matching on packets.
Care should be taken to assure that legitimate users are not
blocked inadvertently. You may wish to list any valid IP address
with the whitelist option in tallow.conf(5). Multiple addresses can
be whitelisted.
## OPTIONS
The `tallow` daemon itself has no runtime configuration. All
configuration is done through the tallow.conf(5) config file.
## SEE ALSO
systemd-journald(1), iptables(1), ipset(1), tallow.conf(5)
## BUGS
`tallow` is `NOT A SECURITY SOLUTION`, nor does it protect against
random password logins. A attacker may still be able to logon to your
systems if you allow password logins.
## AUTHOR
Auke Kok <auke-jan.h.kok@intel.com>
+176 -96
View File
@@ -15,18 +15,27 @@
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdbool.h>
#include <signal.h>
#include <unistd.h>
#include <limits.h>
#include <sys/time.h>
#include <pcre.h>
#include <systemd/sd-journal.h>
#ifdef DEBUG
#define dbg(args...) fprintf(stderr, ##args)
#else
#define dbg(args...) do {} while (0)
#endif
struct tallow_struct {
char *ip;
int count;
float score;
struct timeval time;
struct tallow_struct *next;
bool blocked;
};
static struct tallow_struct *head;
@@ -34,10 +43,29 @@ static struct tallow_struct *head;
static struct tallow_struct *whitelist;
#define FILTER_STRING "SYSLOG_IDENTIFIER=sshd"
struct pattern_struct {
int instant_block;
float weight;
char *pattern;
pcre *re;
};
static char iptables_path[PATH_MAX] = "/usr/sbin";
static char chain[PATH_MAX] = "TALLOW";
static int threshold = 3;
#define PATTERN_COUNT 9
static struct pattern_struct patterns[PATTERN_COUNT] = {
{ 0, 0.4, "MESSAGE=Failed .* for .* from ([0-9a-z:.]+) port \\d+ ssh2", NULL},
{ 0, 0.4, "MESSAGE=error: PAM: Authentication failure for .* from ([0-9a-z:.]+)", NULL},
{15, 0.3, "MESSAGE=Invalid user .* from ([0-9a-z:.]+) port \\d+", NULL},
{15, 0.3, "MESSAGE=Did not receive identification string from ([0-9a-z:.]+) port \\d+", NULL},
{30, 0.3, "MESSAGE=Failed .* for root from ([0-9a-z:.]+) port \\d+ ssh2", NULL},
{15, 0.6, "MESSAGE=Bad protocol version identification .* from ([0-9a-z:.]+)", NULL},
{15, 0.6, "MESSAGE=Connection closed by authenticating user .* ([0-9a-z:.]+) port \\d+", NULL},
{15, 0.6, "MESSAGE=Received disconnect from ([0-9a-z:.]+) port .*\\[preauth\\]", 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 int expires = 3600;
static int has_ipv6 = 0;
static sd_journal *j;
@@ -67,37 +95,73 @@ static void ext_ignore(char *fmt, ...)
vsnprintf(cmd, sizeof(cmd), fmt, args);
va_end(args);
(void) system(cmd);
__attribute__((unused)) int ret = system(cmd);
}
static void block(struct tallow_struct *s)
static void setup(void)
{
if (s->count != threshold)
static bool done = false;
if (done)
return;
done = true;
if (strchr(s->ip, ':')) {
if (has_ipv6)
(void) ext("%s/ip6tables -t filter -A %s -s %s -j DROP", iptables_path, chain, s->ip);
} else {
(void) ext("%s/iptables -t filter -A %s -s %s -j DROP", iptables_path, chain, s->ip);
/* 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);
}
fprintf(stderr, "Blocked %s\n", s->ip);
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/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);
}
}
}
static void unblock(struct tallow_struct *s)
static void block(struct tallow_struct *s, int instant_block)
{
if (s->count < threshold)
return;
setup();
if (strchr(s->ip, ':')) {
if (has_ipv6)
(void) ext("%s/ip6tables -t filter -D %s -s %s -j DROP", iptables_path, chain, s->ip);
if (has_ipv6) {
if (instant_block > 0) {
(void) ext("%s/ipset -! add tallow6 %s timeout %d",
ipt_path, s->ip, instant_block);
} else {
(void) ext("%s/ipset -! add tallow6 %s", ipt_path, s->ip);
s->blocked = true;
}
}
} else {
(void) ext("%s/iptables -t filter -D %s -s %s -j DROP", iptables_path, chain, s->ip);
if (instant_block > 0) {
(void) ext("%s/ipset -! add tallow %s timeout %d",
ipt_path, s->ip, instant_block);
} else {
(void) ext("%s/ipset -! add tallow %s", ipt_path, s->ip);
s->blocked = true;
}
}
fprintf(stderr, "Unblocked %s\n", s->ip);
if (s->blocked) {
fprintf(stderr, "Blocked %s\n", s->ip);
} else {
dbg("Throttled %s\n", s->ip);
}
}
static void whitelist_add(char *ip)
@@ -108,14 +172,15 @@ static void whitelist_add(char *ip)
while (w && w->next)
w = w->next;
n = malloc(sizeof(struct tallow_struct));
n = calloc(1, sizeof(struct tallow_struct));
if (!n) {
fprintf(stderr, "Out of memory.\n");
exit(1);
exit(EXIT_FAILURE);
}
memset(n, 0, sizeof(struct tallow_struct));
n->ip = strdup(ip);
n->next = NULL;
n->blocked = false;
if (!whitelist)
whitelist = n;
@@ -123,12 +188,15 @@ static void whitelist_add(char *ip)
w->next = n;
}
static void find(char *ip)
static void find(const char *ip, float weight, int instant_block)
{
struct tallow_struct *s = head;
struct tallow_struct *n;
struct tallow_struct *w = whitelist;
if (!ip)
return;
/*
* not validating the IP address format here, just
* making sure we're not passing special characters
@@ -147,10 +215,20 @@ static void find(char *ip)
/* walk and update entry */
while (s) {
if (!strcmp(s->ip, ip)) {
s->count++;
s->score += weight;
dbg("%s: %1.3f\n", s->ip, s->score);
(void) gettimeofday(&s->time, NULL);
block(s);
if (s->blocked) {
return;
}
if (s->score >= 1.0) {
block(s, 0);
} else if (instant_block > 0) {
block(s, instant_block);
}
return;
}
@@ -161,12 +239,11 @@ static void find(char *ip)
}
/* append */
n = malloc(sizeof(struct tallow_struct));
n = calloc(1, sizeof(struct tallow_struct));
if (!n) {
fprintf(stderr, "Out of memory.\n");
exit(1);
exit(EXIT_FAILURE);
}
memset(n, 0, sizeof(struct tallow_struct));
if (!head)
head = n;
@@ -174,43 +251,44 @@ static void find(char *ip)
s->next = n;
n->ip = strdup(ip);
n->count = 1;
n->score = weight;
n->next = NULL;
n->blocked = false;
(void) gettimeofday(&n->time, NULL);
dbg("%s: %1.3f\n", n->ip, n->score);
block(n);
if (weight >= 1.0) {
block(n, 0);
} else if (instant_block > 0) {
block(n, instant_block);
}
return;
}
static void dump(void)
static void sig(int u __attribute__ ((unused)))
{
struct tallow_struct *s = head;
fprintf(stderr, "Received SIGUSR1 - dumping address table: address: count, time\n");
fprintf(stderr, "Exiting on request.\n");
sd_journal_close(j);
struct tallow_struct *s = head;
while (s) {
fprintf(stderr, "%s: %d, %lu.%lu\n", s->ip, s->count, s->time.tv_sec, s->time.tv_usec);
struct tallow_struct *n = NULL;
free(s->ip);
n = s;
s = s->next;
free(n);
}
exit(EXIT_SUCCESS);
}
static void sig(int s)
static void sigusr1(int u __attribute__ ((unused)))
{
if (s == SIGUSR1) {
dump();
} else {
fprintf(stderr, "Exiting on request.\n");
sd_journal_close(j);
struct tallow_struct *s = head;
while (s) {
struct tallow_struct *n = NULL;
free(s->ip);
n = s;
s = s->next;
free(n);
}
exit(0);
fprintf(stderr, "Dumping score list on request:\n");
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;
}
}
@@ -224,16 +302,22 @@ static void prune(void)
p = NULL;
while (s) {
if ((tv.tv_sec - s->time.tv_sec) > expires) {
/*
* 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) {
unblock(s);
p->next = s->next;
free(s->ip);
free(s);
s = p->next;
continue;
} else {
unblock(s);
head = s->next;
free(s->ip);
free(s);
@@ -247,20 +331,25 @@ static void prune(void)
}
}
int main(int argc, char *argv[])
int main(void)
{
int r;
FILE *f;
struct sigaction s;
int timeout = 60;
strcpy(ipt_path, "/usr/sbin");
memset(&s, 0, sizeof(struct sigaction));
s.sa_handler = sig;
sigaction(SIGUSR1, &s, NULL);
sigaction(SIGHUP, &s, NULL);
sigaction(SIGTERM, &s, NULL);
sigaction(SIGINT, &s, NULL);
memset(&s, 0, sizeof(struct sigaction));
s.sa_handler = sigusr1;
sigaction(SIGUSR1, &s, NULL);
if (access("/proc/sys/net/ipv6", R_OK | X_OK) == 0)
has_ipv6 = 1;
@@ -287,13 +376,8 @@ int main(int argc, char *argv[])
continue;
// todo: filter leading/trailing whitespace
if (!strcmp(key, "iptables_path"))
strncpy(iptables_path, val, PATH_MAX - 1);
if (!strcmp(key, "chain"))
strncpy(chain, val, PATH_MAX - 1);
if (!strcmp(key, "threshold"))
threshold = atoi(val);
if (!strcmp(key, "ipt_path"))
strncpy(ipt_path, val, PATH_MAX - 1);
if (!strcmp(key, "expires"))
expires = atoi(val);
if (!strcmp(key, "whitelist"))
@@ -313,35 +397,32 @@ int main(int argc, char *argv[])
r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
if (r < 0) {
fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
exit(1);
exit(EXIT_FAILURE);
}
/* init ip(6)tables chains */
ext_ignore("%s/iptables -t filter -N %s > /dev/null 2>&1", iptables_path, chain);
if (ext("%s/iptables -t filter -F %s", iptables_path, chain)) {
fprintf(stderr, "Unable to create/flush iptables chain \"%s\".\n", chain);
exit(1);
}
if (has_ipv6) {
ext_ignore("%s/ip6tables -t filter -N %s > /dev/null 2>&1", iptables_path, chain);
if (ext("%s/ip6tables -t filter -F %s", iptables_path, chain)) {
fprintf(stderr, "Unable to create/flush ip6tables chain \"%s\".\n", chain);
exit(1);
}
}
/* ffwd journal */
sd_journal_add_match(j, FILTER_STRING, 0);
r = sd_journal_seek_tail(j);
sd_journal_wait(j, (uint64_t) 0);
fprintf(stderr, "sd_journal_seek_tail() returned %d\n", r);
dbg("sd_journal_seek_tail() returned %d\n", r);
while (sd_journal_next(j) != 0)
r++;
fprintf(stderr, "Forwarded through %d items in the journal to reach the end\n", r);
dbg("Forwarded through %d items in the journal to reach the end\n", r);
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;
size_t l;
@@ -353,9 +434,7 @@ int main(int argc, char *argv[])
}
while (sd_journal_next(j) != 0) {
char *t;
char *m;
int i;
if (sd_journal_get_data(j, "MESSAGE", &d, &l) < 0) {
fprintf(stderr, "Failed to read message field: %s\n", strerror(-r));
@@ -365,21 +444,22 @@ int main(int argc, char *argv[])
m = strndup(d, l+1);
m[l] = '\0';
if (strstr(m, "MESSAGE=Failed password for invalid user ")) {
t = strtok(m, " ");
for (i = 0; i < 7; i++)
t = strtok(NULL, " ");
find(t);
}
if (strstr(m, "MESSAGE=Failed password for root ")) {
t = strtok(m, " ");
for (i = 0; i < 5; i++)
t = strtok(NULL, " ");
find(t);
for (int i = 0; i < PATTERN_COUNT; i++) {
int 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, patterns[i].pattern);
find(s, patterns[i].weight, patterns[i].instant_block);
pcre_free_substring(s);
}
}
}
free(m);
}
prune();
@@ -387,5 +467,5 @@ int main(int argc, char *argv[])
sd_journal_close(j);
exit(0);
exit(EXIT_SUCCESS);
}
+1 -2
View File
@@ -1,8 +1,7 @@
# tallow.conf - see `man tallow.conf` for more information
#path_iptables=/usr/sbin
#threshold=3
#ipt_path=/usr/sbin
#expires=3600
#whitelist=127.0.0.1
#ipv6=0
+37 -47
View File
@@ -1,47 +1,37 @@
.TH tallow.conf 5 "31 October 2012" ".5" "The tallow configuration file"
.SH NAME
tallow.conf \- Tallow daemon configuration file
.SH SYNOPSIS
/etc/tallow.conf
.SH 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.
.SH OPTIONS
.TP
\fBiptables_path\fR=\<string\>
Specifies the location of the iptables(1) or ip6tables(1) program.
By default, tallow will look in "/usr/sbin" for them.
.TP
\fBchain\fR=\<string\>
Specifies the iptables(1) chain name to use for maintaining the
block list. By default, tallow maintains its iptables(1) rules
in the \fBTALLOW\fR chain.
.TP
\fBexpires\fR=\<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.
.TP
\fBthreshold\fR=\<int\>
Specifies the number of times an IP address may appear before it
is blocked. Defaults to 3.
.TP
\fBwhitelist\fR=\<ipv4 address\>
Specify an IP address 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.
.TP
\fBipv6\fR=\<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.
.SH SEE ALSO
tallow(1), iptables(1)
.SH AUTHOR
Auke Kok <auke-jan.h.kok@intel.com>
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "TALLOW" "5" "December 2017" "" ""
.
.SH "NAME"
\fBtallow\fR
.
.SH "tallow\.conf"
The tallow configuration file
.
.SH "NAME"
tallow\.conf \- Tallow daemon configuration file
.
.SH "SYNOPSIS"
\fB/etc/tallow\.conf\fR
.
.SH "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\.
.
.SH "OPTIONS"
\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<ipv4 address>\fR Specify an IP address 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
\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\.
.
.SH "SEE ALSO"
tallow(1)
.
.SH "AUTHOR"
Auke Kok \fIauke\-jan\.h\.kok@intel\.com\fR
+53
View File
@@ -0,0 +1,53 @@
## 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`=`<ipv4 address>`
Specify an IP address 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.
`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.
## SEE ALSO
tallow(1)
## AUTHOR
Auke Kok <auke-jan.h.kok@intel.com>