4 changed files with 187 additions and 0 deletions
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash |
||||
|
||||
if ! sudo which nft |
||||
then |
||||
echo "install nftables" |
||||
exit 1 |
||||
fi |
||||
|
||||
if ! sudo nft list ruleset |grep output |
||||
then |
||||
sudo nft -f log.nft |
||||
fi |
||||
|
||||
if ! sudo nft list ruleset |grep output |
||||
then |
||||
echo "nft error" |
||||
exit 1 |
||||
fi |
||||
|
||||
if ! id | grep systemd-journal |
||||
then |
||||
echo "sudo adduser $(whoami) systemd-journal" |
||||
sudo adduser "$(whoami)" systemd-journal |
||||
exit 1 |
||||
fi |
||||
|
||||
packages="python3-systemd python3-tabulate python3-dns dnscap python3-pure-pcapy3 tshark" |
||||
|
||||
apt -s install "${packages}" |
||||
echo "apt-get -s ${packages}" |
||||
if apt-get -s install "${packages}" |grep ^Inst |
||||
then |
||||
echo "install python packages" |
||||
exit 1 |
||||
fi |
||||
|
||||
echo "####################" |
||||
echo "### System Ready " |
||||
echo "####################" |
||||
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
[Unit] |
||||
Description=Dnscap Daemon |
||||
After=network-online.target |
||||
|
||||
[Service] |
||||
Type=simple |
||||
User=root |
||||
Group=root |
||||
UMask=007 |
||||
|
||||
ExecStart=/usr/bin/dnscap -b -o pid_file=/var/run/dnscap.pid -i any -p -w - -t 600 -k /usr/bin/tshark -r - -T json | /usr/local/bin/report.py |
||||
Restart=on-failure |
||||
|
||||
# Configures the time to wait before service is stopped forcefully. |
||||
TimeoutStopSec=10 |
||||
|
||||
[Install] |
||||
WantedBy=multi-user.target |
||||
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
#!/usr/sbin/nft -f |
||||
|
||||
flush ruleset |
||||
|
||||
table inet filter { |
||||
chain input { |
||||
type filter hook input priority 0; policy drop; |
||||
iif lo accept |
||||
ct state established,related accept |
||||
# Log and drop invalid packets |
||||
ct state invalid log prefix "INVALID: " level warn drop |
||||
# Log and accept SSH |
||||
tcp dport 22 ct state new log prefix "SSH: " level info accept |
||||
# Allow web traffic (no logging to reduce volume) |
||||
#tcp dport { 80, 443 } accept |
||||
# Log everything else that reaches the end (default drop) |
||||
log prefix "INPUT DROP: " level warn |
||||
} |
||||
|
||||
chain forward { |
||||
type filter hook forward priority 0; policy drop; |
||||
|
||||
log prefix "FORWARD DROP: " level warn |
||||
} |
||||
|
||||
chain output { |
||||
type filter hook output priority 0; policy accept; |
||||
# dns request |
||||
udp dport 53 log prefix "DNS:" level info |
||||
# accept local connection |
||||
ip daddr 192.168.122.1 counter accept |
||||
ip daddr 192.168.0.253 counter accept |
||||
ip daddr 192.168.0.254 counter accept |
||||
# counter output packets |
||||
counter comment "Count accepted packets" |
||||
# not logging established |
||||
ct state established,related accept |
||||
# Log and accept output packets |
||||
log prefix "OUTPUT:" level info flags skuid accept |
||||
} |
||||
} |
||||
@ -0,0 +1,89 @@
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python3 |
||||
|
||||
from systemd import journal |
||||
from datetime import datetime, timedelta |
||||
import re # read lines |
||||
# Import smtplib for the actual sending function |
||||
import smtplib |
||||
# Import the email modules we'll need |
||||
from email.message import EmailMessage |
||||
from tabulate import tabulate |
||||
import os |
||||
import socket |
||||
import logging |
||||
import logging.config |
||||
import sys |
||||
import json |
||||
|
||||
class connection: |
||||
def __init__(self,logger): |
||||
self.logger = logger |
||||
self.records = [] |
||||
def __get_list(self): |
||||
liste = [] |
||||
for e in self.records: |
||||
self.logger.debug("interface {} source {} destination {} proto {} dport {} uid {} gid {}".format(e['interface'],e['source'],e['destination'],e['proto'],e['dport'],e['uid'],e['gid'])) |
||||
try: |
||||
self.logger.debug("--- ask reverse lookup for : {}".format(e['destination'])) |
||||
domain, alias, addresslist = socket.gethostbyaddr(e['destination']) |
||||
except socket.herror as he: |
||||
self.logger.debug("--- can't resolv {}".format(e['destination'])) |
||||
domain = 'unknown' |
||||
item = [ e['interface'], e['source'], e['destination'], e['proto'], e['dport'], e['uid'], e['gid'] , domain ] |
||||
if not item in liste: |
||||
liste.append(item) |
||||
self.logger.debug(liste) |
||||
return liste |
||||
def report(self): |
||||
headers = { 'Interface':'', 'Source':'', 'Destination':'', 'Protocol':'', 'DestinationPort':'', 'UID':'', 'GID':'' , 'domain':'' } |
||||
header = headers.keys() |
||||
msg = EmailMessage() |
||||
msg.set_content(tabulate(self.__get_list(),headers)) |
||||
msg['Subject'] = f'[ssh][attempts] Reporting' |
||||
msg['From'] = "{}@{}".format(os.getlogin(),socket.gethostname()) |
||||
msg['To'] = "{}@{}".format(os.getlogin(),'192.168.0.238') |
||||
print(msg) |
||||
#s = smtplib.SMTP('localhost') |
||||
#s.send_message(msg) |
||||
#s.quit() |
||||
def addmessage(self, message:str): |
||||
self.logger.debug("-- scan {}".format(message)) |
||||
nftop = r'''^OUTPUT:IN= OUT=(?P<interface>[a-z0-9]+)[ ]SRC=(?P<source>[0-9.]+)[ ]DST=(?P<destination>[0-9.]+)[ ]LEN=([0-9]+)[ ]TOS=([0-9x]+)[ ]PREC=([0-9x]+)[ ]TTL=([0-9]+)[ ]ID=([0-9]+)[ ]DF[ ]PROTO=(?P<proto>[CDTUP]+)[ ]SPT=([0-9]+)[ ]DPT=(?P<dport>[0-9]+)[ ]WINDOW=([0-9]+)[ ]RES=([0-9x]+)[ ]SYN[ ]URGP=([0-9]+)[ ]UID=(?P<uid>[0-9]+)[ ]GID=(?P<gid>[0-9]+)[ ]+$''' |
||||
nftsp = r'''^SSH:[ ]IN=(?P<interface>[a-z0-9]+)[ ]OUT=[ ]MAC=(?P<gid>[0-9a-f:]+)[ ]SRC=(?P<source>[0-9.]+)[ ]DST=(?P<destination>[0-9.]+)[ ]LEN=([0-9]+)[ ]TOS=([0-9x]+)[ ]PREC=([0-9A-Fx]+)[ ]TTL=([0-9]+)[ ]ID=([0-9]+)[ ]DF[ ]PROTO=(?P<proto>[CDTUP]+)[ ]SPT=([0-9]+)[ ]DPT=(?P<dport>[0-9]+)[ ]WINDOW=([0-9]+)[ ]RES=([0-9x]+)[ ](SYN |ACK )(PSH |)URGP=(?P<uid>[0-9]+)[ ]+$''' |
||||
nftom = re.match(re.compile(nftop),message) |
||||
nftsm = re.match(re.compile(nftsp),message) |
||||
if nftom: |
||||
self.logger.debug("$$$ matched: {}".format(nftom)) |
||||
self.records.append(nftom.groupdict()) |
||||
elif nftsm: |
||||
self.logger.debug("$$$ matched: {}".format(nftsm)) |
||||
self.records.append(nftsm.groupdict()) |
||||
|
||||
# initiate |
||||
logging.basicConfig(filename='/tmp/outlog_reporting.log',level=logging.DEBUG) |
||||
logger=logging.getLogger(__name__) |
||||
c = connection(logger) |
||||
|
||||
# read from dnscap |
||||
try: |
||||
queries = [] |
||||
js = json.load(sys.stdin) |
||||
logger.debug("Json : {}".format(js)) |
||||
for request in js: |
||||
for k,v in request['_source']['layers']['dns']['Queries'].items(): |
||||
query = v['dns.qry.name'] |
||||
logger.debug("query: {}".format(query)) |
||||
queries.append(query) |
||||
logger.debug("queries: {}".format(queries)) |
||||
except json.decoder.JSONDecodeError as jde: |
||||
logger.warning("No json found as input") |
||||
|
||||
# read logs from kernel journal |
||||
j = journal.Reader() |
||||
j.seek_realtime(datetime.now() - timedelta(hours=1)) |
||||
j.add_match('_TRANSPORT=kernel') |
||||
for entry in j: |
||||
logger.debug(entry['MESSAGE']) |
||||
c.addmessage(entry['MESSAGE']) |
||||
logger.debug("--- Generating report") |
||||
c.report() |
||||
Loading…
Reference in new issue