mirror of
https://github.com/clearlinux/tallow.git
synced 2026-06-16 01:15:48 +00:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9174590b04 | |||
| 14152b1dad | |||
| e2f92ff75b | |||
| 4ff1206974 | |||
| 8c836013cd | |||
| a6fb19ff4f | |||
| 5503ff0b20 | |||
| 8655223248 | |||
| 76a59df0f1 | |||
| 36946deeee | |||
| d51577bd4f | |||
| e15bfe4dc0 | |||
| d6e53e6f3a | |||
| a2a6d8e9ac | |||
| c5569176d2 | |||
| bf81c259b0 | |||
| ff2a47756f | |||
| b18f636489 | |||
| 6e0251d3dc | |||
| 60a90adbc5 | |||
| 1f43bcbf12 | |||
| 8da71a2184 | |||
| 38b09c3b07 | |||
| 20f4c970de | |||
| 32c20f190f | |||
| e3e4388654 | |||
| 8a05303802 | |||
| e296f501c7 | |||
| 379f74a071 | |||
| 9042a01eab | |||
| 2225ee029d | |||
| dee23b8275 | |||
| 34bd8d55bd | |||
| 2a33768293 | |||
| ea958fd2b5 | |||
| 4547892d56 | |||
| c661a20e33 | |||
| 9f37520c72 | |||
| dc8f37e41f | |||
| d590c8f67f | |||
| ec2b5cbfc0 | |||
| cb41c16e93 | |||
| 40568eb4cd | |||
| 992927798d | |||
| fba8921952 | |||
| 73e9cd7011 | |||
| a4d9d9688e | |||
| 35eeabb146 | |||
| 08d45d39fd |
+4
-2
@@ -11,6 +11,8 @@ install-sh
|
||||
missing
|
||||
tallow
|
||||
tallow-*.tar.gz
|
||||
tallow-1/
|
||||
tallow.o
|
||||
tallow-*/
|
||||
*.o
|
||||
tallow.service
|
||||
*~
|
||||
DEADJOE
|
||||
|
||||
+21
@@ -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
|
||||
+26
-6
@@ -1,17 +1,37 @@
|
||||
|
||||
AM_CFLAGS = -g $(LIBSYSTEMD_CFLAGS) -Wall -Wno-uninitialized
|
||||
AM_CFLAGS = -g \
|
||||
$(JSON_C_CFLAGS) $(PCRE_CFLAGS) $(LIBSYSTEMD_CFLAGS) \
|
||||
-Wall -Wno-uninitialized -W -D_FORTIFY_SOURCE=2
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-DDATADIR='"$(datadir)"' -DSYSCONFDIR='"$(sysconfdir)"'
|
||||
|
||||
systemdsystemunitdir = @SYSTEMD_SYSTEMUNITDIR@
|
||||
systemdsystemunit_DATA = tallow.service
|
||||
systemdsystemunit_DATA = data/tallow.service
|
||||
|
||||
sbin_PROGRAMS = tallow
|
||||
tallow_SOURCES = tallow.c
|
||||
tallow_LDADD = $(LIBSYSTEMD_LIBS)
|
||||
tallow_SOURCES = tallow.c json.c json.h data.c data.h
|
||||
tallow_LDADD = $(JSON_C_LIBS) $(PCRE_LIBS) $(LIBSYSTEMD_LIBS)
|
||||
|
||||
EXTRA_DIST = AUTHORS COPYING INSTALL tallow.service.in
|
||||
pkgdata_DATA = data/sshd.json
|
||||
|
||||
EXTRA_DIST = \
|
||||
AUTHORS COPYING INSTALL README.md \
|
||||
data/tallow.service.in \
|
||||
data/sshd.json \
|
||||
tallow.conf.5.md \
|
||||
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: tallow.conf.5.md
|
||||
ronn -r tallow.conf.5.md --pipe > tallow.conf.5
|
||||
|
||||
tallow.1: tallow.1.md
|
||||
ronn -r tallow.1.md --pipe > tallow.1
|
||||
|
||||
+8
-4
@@ -2,17 +2,21 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ([2.64])
|
||||
AC_INIT([tallow], [1], [auke-jan.h.kok@intel.com])
|
||||
AM_INIT_AUTOMAKE([])
|
||||
AC_INIT([tallow], [15], [auke-jan.h.kok@intel.com])
|
||||
AM_INIT_AUTOMAKE([foreign])
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
|
||||
PKG_CHECK_MODULES([LIBSYSTEMD], [libsystemd])
|
||||
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)
|
||||
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}],
|
||||
@@ -25,5 +29,5 @@ fi
|
||||
AC_CHECK_HEADERS([stdlib.h stdio.h string.h stdarg.h limits.h sys/time.h])
|
||||
|
||||
AC_OUTPUT([
|
||||
tallow.service
|
||||
data/tallow.service
|
||||
])
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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 <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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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);
|
||||
@@ -0,0 +1,57 @@
|
||||
[
|
||||
{
|
||||
"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."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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 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 (int 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 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 (int 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 %s\n", sp);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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,41 +1,40 @@
|
||||
.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" "October 2018" "" ""
|
||||
.
|
||||
.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 "SIGNALS"
|
||||
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)
|
||||
.
|
||||
.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
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
|
||||
## 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.
|
||||
|
||||
## SIGNALS
|
||||
|
||||
The `USR1` signal causes `tallow` to print out it's internal tracking
|
||||
table of IP addresses. This requires that tallow is compiled with
|
||||
the `-DDEBUG=1` symbol passed to the compiler.
|
||||
|
||||
## 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>
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* tallow.c - IP block sshd login abuse
|
||||
*
|
||||
* (C) Copyright 2015 Intel Corporation
|
||||
* (C) Copyright 2015-2019 Intel Corporation
|
||||
* Authors:
|
||||
* Auke Kok <auke-jan.h.kok@intel.com>
|
||||
*
|
||||
@@ -15,31 +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 <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include <pcre.h>
|
||||
#include <systemd/sd-journal.h>
|
||||
|
||||
struct tallow_struct {
|
||||
char *ip;
|
||||
int count;
|
||||
struct timeval time;
|
||||
struct tallow_struct *next;
|
||||
};
|
||||
#include "json.h"
|
||||
#include "data.h"
|
||||
|
||||
static struct tallow_struct *head;
|
||||
#define MAX_OFFSETS 30
|
||||
|
||||
static struct tallow_struct *whitelist;
|
||||
|
||||
#define FILTER_STRING "SYSLOG_IDENTIFIER=sshd"
|
||||
|
||||
static char iptables_path[PATH_MAX] = "/usr/sbin";
|
||||
static char chain[PATH_MAX] = "TALLOW";
|
||||
static int threshold = 3;
|
||||
static char ipt_path[PATH_MAX];
|
||||
static int expires = 3600;
|
||||
static int has_ipv6 = 0;
|
||||
static bool nocreate = false;
|
||||
static sd_journal *j;
|
||||
|
||||
static int ext(char *fmt, ...)
|
||||
@@ -67,68 +63,85 @@ 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 (nocreate)
|
||||
return;
|
||||
|
||||
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 block_struct *s, int instant_block)
|
||||
{
|
||||
if (s->count < threshold)
|
||||
setup();
|
||||
|
||||
if (strchr(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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->blocked) {
|
||||
fprintf(stderr, "Blocked %s\n", s->ip);
|
||||
} else {
|
||||
dbg("Throttled %s\n", s->ip);
|
||||
}
|
||||
}
|
||||
|
||||
void find(const char *ip, float weight, int instant_block)
|
||||
{
|
||||
struct block_struct *s = blocks;
|
||||
struct block_struct *n;
|
||||
|
||||
if (!ip)
|
||||
return;
|
||||
|
||||
if (strchr(s->ip, ':')) {
|
||||
if (has_ipv6)
|
||||
(void) ext("%s/ip6tables -t filter -D %s -s %s -j DROP", iptables_path, chain, s->ip);
|
||||
} else {
|
||||
(void) ext("%s/iptables -t filter -D %s -s %s -j DROP", iptables_path, chain, s->ip);
|
||||
}
|
||||
|
||||
fprintf(stderr, "Unblocked %s\n", s->ip);
|
||||
}
|
||||
|
||||
static void whitelist_add(char *ip)
|
||||
{
|
||||
struct tallow_struct *w = whitelist;
|
||||
struct tallow_struct *n;
|
||||
|
||||
while (w && w->next)
|
||||
w = w->next;
|
||||
|
||||
n = malloc(sizeof(struct tallow_struct));
|
||||
if (!n) {
|
||||
fprintf(stderr, "Out of memory.\n");
|
||||
exit(1);
|
||||
}
|
||||
memset(n, 0, sizeof(struct tallow_struct));
|
||||
n->ip = strdup(ip);
|
||||
n->next = NULL;
|
||||
|
||||
if (!whitelist)
|
||||
whitelist = n;
|
||||
else
|
||||
w->next = n;
|
||||
}
|
||||
|
||||
static void find(char *ip)
|
||||
{
|
||||
struct tallow_struct *s = head;
|
||||
struct tallow_struct *n;
|
||||
struct tallow_struct *w = whitelist;
|
||||
|
||||
/*
|
||||
* not validating the IP address format here, just
|
||||
* making sure we're not passing special characters
|
||||
@@ -137,20 +150,26 @@ static void find(char *ip)
|
||||
if (strspn(ip, "0123456789abcdef:.") != strlen(ip))
|
||||
return;
|
||||
|
||||
/* whitelist */
|
||||
while (w) {
|
||||
if (!strcmp(w->ip, ip))
|
||||
return;
|
||||
w = w->next;
|
||||
}
|
||||
if (whitelist_find(ip))
|
||||
return;
|
||||
|
||||
/* 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,110 +180,69 @@ static void find(char *ip)
|
||||
}
|
||||
|
||||
/* append */
|
||||
n = malloc(sizeof(struct tallow_struct));
|
||||
n = calloc(1, sizeof(struct block_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;
|
||||
if (!blocks)
|
||||
blocks = n;
|
||||
else
|
||||
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)
|
||||
#ifdef DEBUG
|
||||
static void sigusr1(int u __attribute__ ((unused)))
|
||||
{
|
||||
struct tallow_struct *s = head;
|
||||
fprintf(stderr, "Received SIGUSR1 - dumping address table: address: count, time\n");
|
||||
|
||||
fprintf(stderr, "Dumping score list on request:\n");
|
||||
struct block_struct *s = blocks;
|
||||
while (s) {
|
||||
fprintf(stderr, "%s: %d, %lu.%lu\n", s->ip, s->count, s->time.tv_sec, s->time.tv_usec);
|
||||
fprintf(stderr, "%ld %s %1.3f\n", s->time.tv_sec, s->ip, s->score);
|
||||
s = s->next;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void sig(int s)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void prune(void)
|
||||
{
|
||||
struct tallow_struct *s = head;
|
||||
struct tallow_struct *p;
|
||||
struct timeval tv;
|
||||
|
||||
(void) gettimeofday(&tv, NULL);
|
||||
p = NULL;
|
||||
|
||||
while (s) {
|
||||
if ((tv.tv_sec - s->time.tv_sec) > expires) {
|
||||
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);
|
||||
s = head;
|
||||
p = NULL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
p = s;
|
||||
s = s->next;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
int main(void)
|
||||
{
|
||||
int r;
|
||||
FILE *f;
|
||||
struct sigaction s;
|
||||
int timeout = 60;
|
||||
|
||||
json_load_patterns();
|
||||
|
||||
strcpy(ipt_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));
|
||||
s.sa_handler = sig;
|
||||
s.sa_handler = sigusr1;
|
||||
sigaction(SIGUSR1, &s, NULL);
|
||||
sigaction(SIGHUP, &s, NULL);
|
||||
sigaction(SIGTERM, &s, NULL);
|
||||
sigaction(SIGINT, &s, NULL);
|
||||
#endif
|
||||
|
||||
if (access("/proc/sys/net/ipv6", R_OK | X_OK) == 0)
|
||||
has_ipv6 = 1;
|
||||
|
||||
f = fopen("/etc/tallow.conf", "r");
|
||||
f = fopen(SYSCONFDIR "/tallow.conf", "r");
|
||||
if (f) {
|
||||
char buf[256];
|
||||
char *key;
|
||||
@@ -287,19 +265,16 @@ 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"))
|
||||
whitelist_add(val);
|
||||
if (!strcmp(key, "ipv6"))
|
||||
has_ipv6 = atoi(val);
|
||||
if (!strcmp(key, "nocreate"))
|
||||
nocreate = (atoi(val) == 1);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
@@ -307,40 +282,34 @@ int main(int argc, char *argv[])
|
||||
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) {
|
||||
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);
|
||||
/* add all filters */
|
||||
struct filter_struct *flt = filters;
|
||||
while (flt) {
|
||||
sd_journal_add_match(j, flt->filter, 0);
|
||||
flt = flt->next;
|
||||
}
|
||||
|
||||
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);
|
||||
/* go to the tail and wait */
|
||||
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");
|
||||
fprintf(stderr, PACKAGE_STRING " Started\n");
|
||||
|
||||
for (;;) {
|
||||
const void *d;
|
||||
@@ -350,42 +319,47 @@ int main(int argc, char *argv[])
|
||||
if (r == SD_JOURNAL_INVALIDATE) {
|
||||
fprintf(stderr, "Journal was rotated, resetting\n");
|
||||
sd_journal_seek_tail(j);
|
||||
} else if (r == SD_JOURNAL_NOP) {
|
||||
dbg("Timeout reached, waiting again\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
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));
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
struct pattern_struct *pat = patterns;
|
||||
while (pat) {
|
||||
int off[MAX_OFFSETS];
|
||||
int ret = pcre_exec(pat->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);
|
||||
pcre_free_substring(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (strstr(m, "MESSAGE=Failed password for root ")) {
|
||||
t = strtok(m, " ");
|
||||
for (i = 0; i < 5; i++)
|
||||
t = strtok(NULL, " ");
|
||||
find(t);
|
||||
pat = pat->next;
|
||||
}
|
||||
|
||||
free(m);
|
||||
|
||||
}
|
||||
|
||||
prune();
|
||||
prune(expires);
|
||||
}
|
||||
|
||||
sd_journal_close(j);
|
||||
|
||||
exit(0);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
+1
-2
@@ -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
|
||||
|
||||
+57
-47
@@ -1,47 +1,57 @@
|
||||
.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" "October 2018" "" ""
|
||||
.
|
||||
.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<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\.
|
||||
.
|
||||
.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\.
|
||||
.
|
||||
.P
|
||||
\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
|
||||
|
||||
ip6tables \-t filter \-I INPUT \-m set \-\-match\-set tallow6 src \-j DROP
|
||||
ipset create tallow6 hash:ip family inet6 timeout 3600
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "SEE ALSO"
|
||||
tallow(1)
|
||||
.
|
||||
.SH "AUTHOR"
|
||||
Auke Kok \fIauke\-jan\.h\.kok@intel\.com\fR
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
|
||||
## 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, 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 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>
|
||||
|
||||
Reference in New Issue
Block a user