Python3 update

This commit is contained in:
Mario Fetka
2026-04-22 23:24:29 +02:00
parent 6db8ad7a2d
commit 2230ffdadd
48 changed files with 637 additions and 2095 deletions

17
debian/README.Debian vendored
View File

@@ -1,6 +1,19 @@
fail2ban-p2p for Debian
-----------------------
<possible notes regarding this package - if none, delete this file>
This package ships the legacy fail2ban-p2p daemon and client, updated to run
with Python 3. The upstream project is old and its packaging layout is unusual,
so this Debian packaging intentionally keeps the service wiring conservative.
-- Manuel Munz <manu@somakoma.de> Wed, 07 Nov 2012 16:40:08 +0100
Operational notes:
* The daemon is disabled by default. Enable it in /etc/default/fail2ban-p2p.
* Configuration lives in /etc/fail2ban-p2p/.
* The package creates a dedicated system user: fail2ban-p2p.
* A local keypair can be generated with:
fail2ban-p2p.py -K -c /etc/fail2ban-p2p
* The daemon log file is:
/var/log/fail2ban-p2p.log
The shipped init script is retained for compatibility with older setups. For
new deployments, a native systemd unit would be preferable.

17
debian/README.source vendored
View File

@@ -1,9 +1,14 @@
fail2ban-p2p for Debian
-----------------------
<this file describes information about the source package, see Debian policy
manual section 4.14. You WILL either need to modify or delete this file>
fail2ban-p2p source package for Debian
--------------------------------------
This package is maintained as a minimal refresh of the historical Debian
packaging while the upstream codebase is being ported to Python 3.
Notes for maintainers:
* The package still installs the application using setup.py because the
upstream layout is not yet a standard Python package layout.
* Many files generated by dh_make and debhelper were intentionally removed
from debian/ because they were examples or build artefacts.
* If the upstream package layout is modernized later, debian/rules should be
simplified further to use pybuild directly.

10
debian/changelog vendored
View File

@@ -1,3 +1,13 @@
fail2ban-p2p (0.1.2+py3) unstable; urgency=medium
* Port package and installed scripts to Python 3.
* Replace Python 2 packaging metadata with dh-python based dependencies.
* Update debhelper compatibility for current Debian packaging.
* Refresh maintainer scripts and service metadata for current policy.
* Convert debian/copyright to machine-readable DEP-5 format.
-- Manuel Munz <manu@somakoma.de> Wed, 22 Apr 2026 12:00:00 +0200
fail2ban-p2p (0.1.2) precise; urgency=low
* Better input filtering and error messages for invalid messages

2
debian/compat vendored
View File

@@ -1 +1 @@
8
13

27
debian/control vendored
View File

@@ -2,18 +2,23 @@ Source: fail2ban-p2p
Section: net
Priority: optional
Maintainer: Manuel Munz <manu@somakoma.de>
Build-Depends: debhelper (>= 8.0.0), python (>= 2.5.4-1~)
Build-Depends-Indep: python-central (>= 0.5.6)
XS-Python-Version: current, >= 2.4
Standards-Version: 3.9.2
#Homepage:
#Vcs-Git: git://git.debian.org/collab-maint/fail2ban-p2p.git
#Vcs-Browser: http://git.debian.org/?p=collab-maint/fail2ban-p2p.git;a=summary
Build-Depends:
debhelper (>= 13),
dh-python,
python3-all,
python3-setuptools
Standards-Version: 4.7.4.1
Rules-Requires-Root: no
Homepage: https://github.com/mmunz/fail2ban-p2p
Package: fail2ban-p2p
Architecture: all
Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, python-m2crypto, python-argparse, adduser
XB-Python-Version: ${python:Versions}
Depends:
${misc:Depends},
${python3:Depends},
adduser,
python3-m2crypto
Description: Distribute attacker information from fail2ban via a p2p network
Fail2ban-p2p can be used to distribute information about atackers in a
p2p/f2f network to ban these attackers on all hosts.
fail2ban-p2p distributes attacker information from fail2ban between hosts
in a peer-to-peer or friend-to-friend network so attackers can be blocked
across multiple systems.

72
debian/copyright vendored
View File

@@ -1,26 +1,56 @@
This package was originally debianized by Manuel Munz
<manu@somakoma.de> on Wed Nov 11 15:50:00 HST 2012
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: fail2ban-p2p
Source: https://github.com/mmunz/fail2ban-p2p
Copyright: 2012- Manuel Munz/Johannes Fürmann
Files: *
Copyright: 2012-2013 Johannes Fuermann <johannes at fuermann.cc>
2012-2013 Manuel Munz <manu@somakoma.de>
License: GPL-3+
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, either version 3 of the License, or
(at your option) any later version.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Comment:
The upstream source files state that fail2ban-p2p is licensed under the
GNU General Public License Version 3.
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; either version 2 of the License, or
(at your option) any later version.
Files: odict.py
Copyright: 2009 Raymond Hettinger
License: MIT
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Files: debian/*
Copyright: 2012-2026 Manuel Munz <manu@somakoma.de>
License: GPL-3+
You should have received a copy of the GNU General Public License
along with this program; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301, USA.
On Debian systems, the complete text of the GNU General Public
License, version 2, can be found in /usr/share/common-licenses/GPL-2.
The Debian packaging is (C) 2011-, Manuel Munz <manu@somakoma.de>
and is licensed under the GPL, see above.
License: GPL-3+
On Debian systems, the full text of the GNU General Public License
version 3 can be found in /usr/share/common-licenses/GPL-3.
License: MIT
On Debian systems, the full text of the MIT license can be found in
/usr/share/common-licenses/MIT.

View File

@@ -1,45 +0,0 @@
#! /bin/sh -e
# /usr/lib/emacsen-common/packages/install/fail2ban-p2p
# Written by Jim Van Zandt <jrv@debian.org>, borrowing heavily
# from the install scripts for gettext by Santiago Vila
# <sanvila@ctv.es> and octave by Dirk Eddelbuettel <edd@debian.org>.
FLAVOR=$1
PACKAGE=fail2ban-p2p
if [ ${FLAVOR} = emacs ]; then exit 0; fi
echo install/${PACKAGE}: Handling install for emacsen flavor ${FLAVOR}
#FLAVORTEST=`echo $FLAVOR | cut -c-6`
#if [ ${FLAVORTEST} = xemacs ] ; then
# SITEFLAG="-no-site-file"
#else
# SITEFLAG="--no-site-file"
#fi
FLAGS="${SITEFLAG} -q -batch -l path.el -f batch-byte-compile"
ELDIR=/usr/share/emacs/site-lisp/${PACKAGE}
ELCDIR=/usr/share/${FLAVOR}/site-lisp/${PACKAGE}
# Install-info-altdir does not actually exist.
# Maybe somebody will write it.
if test -x /usr/sbin/install-info-altdir; then
echo install/${PACKAGE}: install Info links for ${FLAVOR}
install-info-altdir --quiet --section "" "" --dirname=${FLAVOR} /usr/share/info/${PACKAGE}.info.gz
fi
install -m 755 -d ${ELCDIR}
cd ${ELDIR}
FILES=`echo *.el`
cp ${FILES} ${ELCDIR}
cd ${ELCDIR}
cat << EOF > path.el
(setq load-path (cons "." load-path) byte-compile-warnings nil)
EOF
${FLAVOR} ${FLAGS} ${FILES}
rm -f *.el path.el
exit 0

View File

@@ -1,15 +0,0 @@
#!/bin/sh -e
# /usr/lib/emacsen-common/packages/remove/fail2ban-p2p
FLAVOR=$1
PACKAGE=fail2ban-p2p
if [ ${FLAVOR} != emacs ]; then
if test -x /usr/sbin/install-info-altdir; then
echo remove/${PACKAGE}: removing Info links for ${FLAVOR}
install-info-altdir --quiet --remove --dirname=${FLAVOR} /usr/share/info/fail2ban-p2p.info.gz
fi
echo remove/${PACKAGE}: purging byte-compiled files for ${FLAVOR}
rm -rf /usr/share/${FLAVOR}/site-lisp/${PACKAGE}
fi

View File

@@ -1,25 +0,0 @@
;; -*-emacs-lisp-*-
;;
;; Emacs startup file, e.g. /etc/emacs/site-start.d/50fail2ban-p2p.el
;; for the Debian fail2ban-p2p package
;;
;; Originally contributed by Nils Naumann <naumann@unileoben.ac.at>
;; Modified by Dirk Eddelbuettel <edd@debian.org>
;; Adapted for dh-make by Jim Van Zandt <jrv@debian.org>
;; The fail2ban-p2p package follows the Debian/GNU Linux 'emacsen' policy and
;; byte-compiles its elisp files for each 'emacs flavor' (emacs19,
;; xemacs19, emacs20, xemacs20...). The compiled code is then
;; installed in a subdirectory of the respective site-lisp directory.
;; We have to add this to the load-path:
(let ((package-dir (concat "/usr/share/"
(symbol-name flavor)
"/site-lisp/fail2ban-p2p")))
;; If package-dir does not exist, the fail2ban-p2p package must have
;; removed but not purged, and we should skip the setup.
(when (file-directory-p package-dir)
(setq load-path (cons package-dir load-path))
(autoload 'fail2ban-p2p-mode "fail2ban-p2p-mode"
"Major mode for editing fail2ban-p2p files." t)
(add-to-list 'auto-mode-alist '("\\.fail2ban-p2p$" . fail2ban-p2p-mode))))

View File

@@ -1 +0,0 @@
#DOCS#

View File

@@ -1 +0,0 @@
#DOCS#

View File

@@ -1,4 +0,0 @@
#
# Regular cron jobs for the fail2ban-p2p package
#
0 4 * * * root [ -x /usr/bin/fail2ban-p2p_maintenance ] && /usr/bin/fail2ban-p2p_maintenance

View File

@@ -1,12 +0,0 @@
dh_installdirs
dh_installdocs
dh_installlogrotate
dh_pycentral
dh_installinit
dh_link
dh_compress
dh_fixperms
dh_installdeb
dh_gencontrol
dh_md5sums
dh_builddeb

View File

@@ -1,13 +1,10 @@
# Defaults for fail2ban-p2p initscript
# sourced by /etc/init.d/fail2ban-p2p
# installed at /etc/default/fail2ban-p2p by the maintainer scripts
#
# This is a POSIX shell fragment
#
# Additional options that are passed to the Daemon.
# Additional options passed to the daemon.
DAEMON_OPTS=""
# Set this to true to start the daemon at boot
# Set to true to start the daemon at boot.
# Leave disabled by default so the service is only started after
# configuration and key material have been created by the administrator.
START_DAEMON=false

View File

@@ -1,20 +0,0 @@
Document: fail2ban-p2p
Title: Debian fail2ban-p2p Manual
Author: <insert document author here>
Abstract: This manual describes what fail2ban-p2p is
and how it can be used to
manage online manuals on Debian systems.
Section: unknown
Format: debiandoc-sgml
Files: /usr/share/doc/fail2ban-p2p/fail2ban-p2p.sgml.gz
Format: postscript
Files: /usr/share/doc/fail2ban-p2p/fail2ban-p2p.ps.gz
Format: text
Files: /usr/share/doc/fail2ban-p2p/fail2ban-p2p.text.gz
Format: HTML
Index: /usr/share/doc/fail2ban-p2p/html/index.html
Files: /usr/share/doc/fail2ban-p2p/html/*.html

79
debian/fail2ban-p2p.init vendored Executable file
View File

@@ -0,0 +1,79 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: fail2ban-p2p
# Required-Start: $remote_fs $network $syslog
# Required-Stop: $remote_fs $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: exchange fail2ban ban information between trusted peers
# Description: Starts the fail2ban-p2p daemon that exchanges attacker
# information between trusted nodes.
### END INIT INFO
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="fail2ban-p2p daemon"
NAME=fail2ban-p2p
DAEMON=/usr/bin/fail2ban-p2p.py
DAEMON_USER=fail2ban-p2p
DAEMON_GROUP=fail2ban-p2p
PIDFILE=/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
DAEMON_ARGS=""
[ -x "$DAEMON" ] || exit 0
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
if [ "$START_DAEMON" != "true" ]; then
echo "fail2ban-p2p is disabled in /etc/default/$NAME"
exit 0
fi
. /lib/init/vars.sh
. /lib/lsb/init-functions
do_start() {
start-stop-daemon --start --quiet --background --make-pidfile \
--pidfile "$PIDFILE" --chuid "$DAEMON_USER:$DAEMON_GROUP" \
--exec "$DAEMON" --test > /dev/null || return 1
start-stop-daemon --start --quiet --background --make-pidfile \
--pidfile "$PIDFILE" --chuid "$DAEMON_USER:$DAEMON_GROUP" \
--exec "$DAEMON" -- $DAEMON_ARGS || return 2
}
do_stop() {
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 \
--pidfile "$PIDFILE" --name "$(basename "$DAEMON")"
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
rm -f "$PIDFILE"
return "$RETVAL"
}
case "$1" in
start)
log_daemon_msg "Starting $DESC" "$NAME"
do_start
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
log_end_msg $?
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
restart|force-reload)
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop || true
do_start
log_end_msg $?
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac
exit 0

1
debian/fail2ban-p2p.install vendored Normal file
View File

@@ -0,0 +1 @@
fail2ban-p2p.service lib/systemd/system/

View File

@@ -1,15 +0,0 @@
# Automatically added by dh_pycentral
rm -f /var/lib/pycentral/fail2ban-p2p.pkgremove
if which pycentral >/dev/null 2>&1; then
pycentral pkginstall fail2ban-p2p
if grep -qs '^fail2ban-p2p$' /var/lib/pycentral/delayed-pkgs; then
sed -i '/^fail2ban-p2p$/d' /var/lib/pycentral/delayed-pkgs
fi
fi
# End automatically added section
# Automatically added by dh_installinit
if [ -x "/etc/init.d/fail2ban-p2p" ]; then
update-rc.d fail2ban-p2p defaults 99 >/dev/null
invoke-rc.d fail2ban-p2p start || exit $?
fi
# End automatically added section

View File

@@ -1,5 +0,0 @@
# Automatically added by dh_installinit
if [ "$1" = "purge" ] ; then
update-rc.d fail2ban-p2p remove >/dev/null
fi
# End automatically added section

View File

@@ -1,8 +0,0 @@
# Automatically added by dh_pycentral
case "$1" in
install|upgrade)
mkdir -p /var/lib/pycentral
echo '# the presence of this file allows calling pkgremove on upgrade' \
> /var/lib/pycentral/fail2ban-p2p.pkgremove
esac
# End automatically added section

View File

@@ -1,50 +0,0 @@
# Automatically added by dh_installinit
if [ -x "/etc/init.d/fail2ban-p2p" ]; then
invoke-rc.d fail2ban-p2p stop || exit $?
fi
# End automatically added section
# Automatically added by dh_pycentral
case "$1" in remove|upgrade)
pkgremove=y
esac
if [ -f /var/lib/pycentral/fail2ban-p2p.pkgremove ] || [ -f /var/lib/pycentral/pkgremove ]; then
pkgremove=y
fi
if [ "$pkgremove" = y ]; then
if which python >/dev/null 2>&1 && which pycentral >/dev/null 2>&1; then
pycentral pkgremove fail2ban-p2p
else
flist=$(tempfile)
slist=$(tempfile)
dpkg -L fail2ban-p2p | tee $flist | \
while read n; do
case "$n" in
/usr/share/pyshared/*)
n2=${n#/usr/share/pyshared/*}
case "$n" in
*.py) echo "p $n";;
*) [ -d "$n" ] && echo "d $n2" || echo "f $n2"
esac
;;
*) continue
esac
done > $slist
if [ -s $slist ]; then
for d in /usr/lib/python[0-9].[0-9]/????-packages; do
case "$d" in */python2.1/*|*/python2.2/*) continue; esac
while read t n; do
case "$t" in
p) rm -f $d/$n $d/${n}[co];;
d) rmdir $d/$n 2>/dev/null || true;;
*) rm -f $d/$n
esac
done < $slist
done
fi
awk '/\/usr\/share\/pyshared/ {next} /\.py$/ {print $0"c\n" $0"o"}' $flist \
| xargs -r rm -f >&2
rm -f $flist $slist
fi
rm -f /var/lib/pycentral/fail2ban-p2p.pkgremove
fi
# End automatically added section

25
debian/fail2ban-p2p.service vendored Normal file
View File

@@ -0,0 +1,25 @@
[Unit]
Description=fail2ban-p2p distributed ban exchange daemon
Documentation=man:systemd.service(5)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=fail2ban-p2p
Group=fail2ban-p2p
EnvironmentFile=-/etc/default/fail2ban-p2p
ExecStart=/usr/bin/python3 /usr/share/fail2ban-p2p/fail2ban-p2p.py -c /etc/fail2ban-p2p $DAEMON_OPTS
Restart=on-failure
RestartSec=5s
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=/etc/fail2ban-p2p /var/log/fail2ban-p2p.log
WorkingDirectory=/var/lib/fail2ban-p2p
StateDirectory=fail2ban-p2p
RuntimeDirectory=fail2ban-p2p
[Install]
WantedBy=multi-user.target

View File

@@ -1,3 +0,0 @@
python:Versions=current, >= 2.4
python:Depends=python (>= 2.4), python-central (>= 0.6.11)
misc:Depends=

1
debian/files vendored
View File

@@ -1 +0,0 @@
fail2ban-p2p_0.0.1-1_all.deb net optional

161
debian/init.d vendored
View File

@@ -1,161 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: fail2ban-p2p
# Required-Start: $network $local_fs
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: <Enter a short description of the sortware>
# Description: <Enter a long description of the software>
# <...>
# <...>
### END INIT INFO
# Author: Manuel Munz <manu@somakoma.de>
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC=fail2ban-p2p # Introduce a short description here
NAME=fail2ban-p2p # Introduce the short server's name here
DAEMON=/usr/bin/fail2ban-p2p.py # Introduce the server's location here
DAEMON_ARGS="" # Arguments to run the daemon with
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
# Exit if the package is not installed
[ -x $DAEMON ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
if [ ! "$START_DAEMON" = "true" ]; then
echo "Fail2ban-p2p is not started. Enable it by editing /etc/default/fail2ban-p2p"
exit 0
fi
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon -m -c fail2ban-p2p -g fail2ban-p2p -b --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon -c fail2ban-p2p -g fail2ban-p2p -m -b --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
$DAEMON_ARGS \
|| return 2
# Add code here, if necessary, that waits for the process to be ready
# to handle requests from services started subsequently which depend
# on this one. As a last resort, sleep for some time.
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/10/KILL/5 --pidfile $PIDFILE --name ${NAME}.py
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/10/KILL/9 --exec $DAEMON
[ "$?" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
}
#
# Function that sends a SIGHUP to the daemon/service
#
#VERBOSE="yes"
do_reload() {
#
# If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
do_start
do_stop
return 0
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
#reload|force-reload)
#
# If do_reload() is not implemented then leave this commented out
# and leave 'force-reload' as an alias for 'restart'.
#
#log_daemon_msg "Reloading $DESC" "$NAME"
#do_reload
#log_end_msg $?
#;;
restart|force-reload)
#
# If the "reload" option is implemented then remove the
# 'force-reload' alias
#
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac
:

59
debian/manpage.1.ex vendored
View File

@@ -1,59 +0,0 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH FAIL2BAN-P2P SECTION "November 7, 2012"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
.\" .nh disable hyphenation
.\" .hy enable hyphenation
.\" .ad l left justify
.\" .ad b justify to both left and right margins
.\" .nf disable filling
.\" .fi enable filling
.\" .br insert line break
.\" .sp <n> insert n+1 empty lines
.\" for manpage-specific macros, see man(7)
.SH NAME
fail2ban-p2p \- program to do something
.SH SYNOPSIS
.B fail2ban-p2p
.RI [ options ] " files" ...
.br
.B bar
.RI [ options ] " files" ...
.SH DESCRIPTION
This manual page documents briefly the
.B fail2ban-p2p
and
.B bar
commands.
.PP
.\" TeX users may be more comfortable with the \fB<whatever>\fP and
.\" \fI<whatever>\fP escape sequences to invode bold face and italics,
.\" respectively.
\fBfail2ban-p2p\fP is a program that...
.SH OPTIONS
These programs follow the usual GNU command line syntax, with long
options starting with two dashes (`-').
A summary of options is included below.
For a complete description, see the Info files.
.TP
.B \-h, \-\-help
Show summary of options.
.TP
.B \-v, \-\-version
Show version of program.
.SH SEE ALSO
.BR bar (1),
.BR baz (1).
.br
The programs are documented fully by
.IR "The Rise and Fall of a Fooish Bar" ,
available via the Info system.
.SH AUTHOR
fail2ban-p2p was written by <upstream author>.
.PP
This manual page was written by Manuel Munz <manu@somakoma.de>,
for the Debian project (and may be used by others).

154
debian/manpage.sgml.ex vendored
View File

@@ -1,154 +0,0 @@
<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
<!-- Process this file with docbook-to-man to generate an nroff manual
page: `docbook-to-man manpage.sgml > manpage.1'. You may view
the manual page with: `docbook-to-man manpage.sgml | nroff -man |
less'. A typical entry in a Makefile or Makefile.am is:
manpage.1: manpage.sgml
docbook-to-man $< > $@
The docbook-to-man binary is found in the docbook-to-man package.
Please remember that if you create the nroff version in one of the
debian/rules file targets (such as build), you will need to include
docbook-to-man in your Build-Depends control field.
-->
<!-- Fill in your name for FIRSTNAME and SURNAME. -->
<!ENTITY dhfirstname "<firstname>FIRSTNAME</firstname>">
<!ENTITY dhsurname "<surname>SURNAME</surname>">
<!-- Please adjust the date whenever revising the manpage. -->
<!ENTITY dhdate "<date>November 7, 2012</date>">
<!-- SECTION should be 1-8, maybe w/ subsection other parameters are
allowed: see man(7), man(1). -->
<!ENTITY dhsection "<manvolnum>SECTION</manvolnum>">
<!ENTITY dhemail "<email>manu@somakoma.de</email>">
<!ENTITY dhusername "Manuel Munz">
<!ENTITY dhucpackage "<refentrytitle>FAIL2BAN-P2P</refentrytitle>">
<!ENTITY dhpackage "fail2ban-p2p">
<!ENTITY debian "<productname>Debian</productname>">
<!ENTITY gnu "<acronym>GNU</acronym>">
<!ENTITY gpl "&gnu; <acronym>GPL</acronym>">
]>
<refentry>
<refentryinfo>
<address>
&dhemail;
</address>
<author>
&dhfirstname;
&dhsurname;
</author>
<copyright>
<year>2003</year>
<holder>&dhusername;</holder>
</copyright>
&dhdate;
</refentryinfo>
<refmeta>
&dhucpackage;
&dhsection;
</refmeta>
<refnamediv>
<refname>&dhpackage;</refname>
<refpurpose>program to do something</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>&dhpackage;</command>
<arg><option>-e <replaceable>this</replaceable></option></arg>
<arg><option>--example <replaceable>that</replaceable></option></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>DESCRIPTION</title>
<para>This manual page documents briefly the
<command>&dhpackage;</command> and <command>bar</command>
commands.</para>
<para>This manual page was written for the &debian; distribution
because the original program does not have a manual page.
Instead, it has documentation in the &gnu;
<application>Info</application> format; see below.</para>
<para><command>&dhpackage;</command> is a program that...</para>
</refsect1>
<refsect1>
<title>OPTIONS</title>
<para>These programs follow the usual &gnu; command line syntax,
with long options starting with two dashes (`-'). A summary of
options is included below. For a complete description, see the
<application>Info</application> files.</para>
<variablelist>
<varlistentry>
<term><option>-h</option>
<option>--help</option>
</term>
<listitem>
<para>Show summary of options.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-v</option>
<option>--version</option>
</term>
<listitem>
<para>Show version of program.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>SEE ALSO</title>
<para>bar (1), baz (1).</para>
<para>The programs are documented fully by <citetitle>The Rise and
Fall of a Fooish Bar</citetitle> available via the
<application>Info</application> system.</para>
</refsect1>
<refsect1>
<title>AUTHOR</title>
<para>This manual page was written by &dhusername; &dhemail; for
the &debian; system (and may be used by others). Permission is
granted to copy, distribute and/or modify this document under
the terms of the &gnu; General Public License, Version 2 any
later version published by the Free Software Foundation.
</para>
<para>
On Debian systems, the complete text of the GNU General Public
License can be found in /usr/share/common-licenses/GPL.
</para>
</refsect1>
</refentry>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:2
sgml-indent-data:t
sgml-parent-document:nil
sgml-default-dtd-file:nil
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
-->

291
debian/manpage.xml.ex vendored
View File

@@ -1,291 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!--
`xsltproc -''-nonet \
-''-param man.charmap.use.subset "0" \
-''-param make.year.ranges "1" \
-''-param make.single.year.ranges "1" \
/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \
manpage.xml'
A manual page <package>.<section> will be generated. You may view the
manual page with: nroff -man <package>.<section> | less'. A typical entry
in a Makefile or Makefile.am is:
DB2MAN = /usr/share/sgml/docbook/stylesheet/xsl/docbook-xsl/manpages/docbook.xsl
XP = xsltproc -''-nonet -''-param man.charmap.use.subset "0"
manpage.1: manpage.xml
$(XP) $(DB2MAN) $<
The xsltproc binary is found in the xsltproc package. The XSL files are in
docbook-xsl. A description of the parameters you can use can be found in the
docbook-xsl-doc-* packages. Please remember that if you create the nroff
version in one of the debian/rules file targets (such as build), you will need
to include xsltproc and docbook-xsl in your Build-Depends control field.
Alternatively use the xmlto command/package. That will also automatically
pull in xsltproc and docbook-xsl.
Notes for using docbook2x: docbook2x-man does not automatically create the
AUTHOR(S) and COPYRIGHT sections. In this case, please add them manually as
<refsect1> ... </refsect1>.
To disable the automatic creation of the AUTHOR(S) and COPYRIGHT sections
read /usr/share/doc/docbook-xsl/doc/manpages/authors.html. This file can be
found in the docbook-xsl-doc-html package.
Validation can be done using: `xmllint -''-noout -''-valid manpage.xml`
General documentation about man-pages and man-page-formatting:
man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/
-->
<!-- Fill in your name for FIRSTNAME and SURNAME. -->
<!ENTITY dhfirstname "FIRSTNAME">
<!ENTITY dhsurname "SURNAME">
<!-- dhusername could also be set to "&dhfirstname; &dhsurname;". -->
<!ENTITY dhusername "Manuel Munz">
<!ENTITY dhemail "manu@somakoma.de">
<!-- SECTION should be 1-8, maybe w/ subsection other parameters are
allowed: see man(7), man(1) and
http://www.tldp.org/HOWTO/Man-Page/q2.html. -->
<!ENTITY dhsection "SECTION">
<!-- TITLE should be something like "User commands" or similar (see
http://www.tldp.org/HOWTO/Man-Page/q2.html). -->
<!ENTITY dhtitle "fail2ban-p2p User Manual">
<!ENTITY dhucpackage "FAIL2BAN-P2P">
<!ENTITY dhpackage "fail2ban-p2p">
]>
<refentry>
<refentryinfo>
<title>&dhtitle;</title>
<productname>&dhpackage;</productname>
<authorgroup>
<author>
<firstname>&dhfirstname;</firstname>
<surname>&dhsurname;</surname>
<contrib>Wrote this manpage for the Debian system.</contrib>
<address>
<email>&dhemail;</email>
</address>
</author>
</authorgroup>
<copyright>
<year>2007</year>
<holder>&dhusername;</holder>
</copyright>
<legalnotice>
<para>This manual page was written for the Debian system
(and may be used by others).</para>
<para>Permission is granted to copy, distribute and/or modify this
document under the terms of the GNU General Public License,
Version 2 or (at your option) any later version published by
the Free Software Foundation.</para>
<para>On Debian systems, the complete text of the GNU General Public
License can be found in
<filename>/usr/share/common-licenses/GPL</filename>.</para>
</legalnotice>
</refentryinfo>
<refmeta>
<refentrytitle>&dhucpackage;</refentrytitle>
<manvolnum>&dhsection;</manvolnum>
</refmeta>
<refnamediv>
<refname>&dhpackage;</refname>
<refpurpose>program to do something</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>&dhpackage;</command>
<!-- These are several examples, how syntaxes could look -->
<arg choice="plain"><option>-e <replaceable>this</replaceable></option></arg>
<arg choice="opt"><option>--example=<parameter>that</parameter></option></arg>
<arg choice="opt">
<group choice="req">
<arg choice="plain"><option>-e</option></arg>
<arg choice="plain"><option>--example</option></arg>
</group>
<replaceable class="option">this</replaceable>
</arg>
<arg choice="opt">
<group choice="req">
<arg choice="plain"><option>-e</option></arg>
<arg choice="plain"><option>--example</option></arg>
</group>
<group choice="req">
<arg choice="plain"><replaceable>this</replaceable></arg>
<arg choice="plain"><replaceable>that</replaceable></arg>
</group>
</arg>
</cmdsynopsis>
<cmdsynopsis>
<command>&dhpackage;</command>
<!-- Normally the help and version options make the programs stop
right after outputting the requested information. -->
<group choice="opt">
<arg choice="plain">
<group choice="req">
<arg choice="plain"><option>-h</option></arg>
<arg choice="plain"><option>--help</option></arg>
</group>
</arg>
<arg choice="plain">
<group choice="req">
<arg choice="plain"><option>-v</option></arg>
<arg choice="plain"><option>--version</option></arg>
</group>
</arg>
</group>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1 id="description">
<title>DESCRIPTION</title>
<para>This manual page documents briefly the
<command>&dhpackage;</command> and <command>bar</command>
commands.</para>
<para>This manual page was written for the Debian distribution
because the original program does not have a manual page.
Instead, it has documentation in the GNU <citerefentry>
<refentrytitle>info</refentrytitle>
<manvolnum>1</manvolnum>
</citerefentry> format; see below.</para>
<para><command>&dhpackage;</command> is a program that...</para>
</refsect1>
<refsect1 id="options">
<title>OPTIONS</title>
<para>The program follows the usual GNU command line syntax,
with long options starting with two dashes (`-'). A summary of
options is included below. For a complete description, see the
<citerefentry>
<refentrytitle>info</refentrytitle>
<manvolnum>1</manvolnum>
</citerefentry> files.</para>
<variablelist>
<!-- Use the variablelist.term.separator and the
variablelist.term.break.after parameters to
control the term elements. -->
<varlistentry>
<term><option>-e <replaceable>this</replaceable></option></term>
<term><option>--example=<replaceable>that</replaceable></option></term>
<listitem>
<para>Does this and that.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-h</option></term>
<term><option>--help</option></term>
<listitem>
<para>Show summary of options.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-v</option></term>
<term><option>--version</option></term>
<listitem>
<para>Show version of program.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1 id="files">
<title>FILES</title>
<variablelist>
<varlistentry>
<term><filename>/etc/foo.conf</filename></term>
<listitem>
<para>The system-wide configuration file to control the
behaviour of <application>&dhpackage;</application>. See
<citerefentry>
<refentrytitle>foo.conf</refentrytitle>
<manvolnum>5</manvolnum>
</citerefentry> for further details.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>${HOME}/.foo.conf</filename></term>
<listitem>
<para>The per-user configuration file to control the
behaviour of <application>&dhpackage;</application>. See
<citerefentry>
<refentrytitle>foo.conf</refentrytitle>
<manvolnum>5</manvolnum>
</citerefentry> for further details.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1 id="environment">
<title>ENVIONMENT</title>
<variablelist>
<varlistentry>
<term><envar>FOO_CONF</envar></term>
<listitem>
<para>If used, the defined file is used as configuration
file (see also <xref linkend="files"/>).</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1 id="diagnostics">
<title>DIAGNOSTICS</title>
<para>The following diagnostics may be issued
on <filename class="devicefile">stderr</filename>:</para>
<variablelist>
<varlistentry>
<term><errortext>Bad configuration file. Exiting.</errortext></term>
<listitem>
<para>The configuration file seems to contain a broken configuration
line. Use the <option>--verbose</option> option, to get more info.
</para>
</listitem>
</varlistentry>
</variablelist>
<para><command>&dhpackage;</command> provides some return codes, that can
be used in scripts:</para>
<segmentedlist>
<segtitle>Code</segtitle>
<segtitle>Diagnostic</segtitle>
<seglistitem>
<seg><errorcode>0</errorcode></seg>
<seg>Program exited successfully.</seg>
</seglistitem>
<seglistitem>
<seg><errorcode>1</errorcode></seg>
<seg>The configuration file seems to be broken.</seg>
</seglistitem>
</segmentedlist>
</refsect1>
<refsect1 id="bugs">
<!-- Or use this section to tell about upstream BTS. -->
<title>BUGS</title>
<para>The program is currently limited to only work
with the <package>foobar</package> library.</para>
<para>The upstreams <acronym>BTS</acronym> can be found
at <ulink url="http://bugzilla.foo.tld"/>.</para>
</refsect1>
<refsect1 id="see_also">
<title>SEE ALSO</title>
<!-- In alpabetical order. -->
<para><citerefentry>
<refentrytitle>bar</refentrytitle>
<manvolnum>1</manvolnum>
</citerefentry>, <citerefentry>
<refentrytitle>baz</refentrytitle>
<manvolnum>1</manvolnum>
</citerefentry>, <citerefentry>
<refentrytitle>foo.conf</refentrytitle>
<manvolnum>5</manvolnum>
</citerefentry></para>
<para>The programs are documented fully by <citetitle>The Rise and
Fall of a Fooish Bar</citetitle> available via the <citerefentry>
<refentrytitle>info</refentrytitle>
<manvolnum>1</manvolnum>
</citerefentry> system.</para>
</refsect1>
</refentry>

2
debian/menu.ex vendored
View File

@@ -1,2 +0,0 @@
?package(fail2ban-p2p):needs="X11|text|vc|wm" section="Applications/see-menu-manual"\
title="fail2ban-p2p" command="/usr/bin/fail2ban-p2p"

48
debian/postinst vendored Normal file → Executable file
View File

@@ -1,42 +1,34 @@
#!/bin/sh
# postinst script for fail2ban-p2p
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <postinst> `configure' <most-recently-configured-version>
# * <old-postinst> `abort-upgrade' <new version>
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
# <new-version>
# * <postinst> `abort-remove'
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
# <failed-install-package> <version> `removing'
# <conflicting-package> <version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
PACKAGE=fail2ban-p2p
SERVER_USER=${SERVER_USER:-fail2ban-p2p}
SERVER_GROUP=${SERVER_GROUP:-fail2ban-p2p}
SERVER_HOME=${SERVER_HOME:-/var/lib/fail2ban-p2p}
LOGFILE=/var/log/fail2ban-p2p.log
CONFDIR=/etc/fail2ban-p2p
case "$1" in
configure)
[ -z "$SERVER_USER" ] && SERVER_USER="fail2ban-p2p"
# fix permissions on /etc/fail2ban-p2p
chown $SERVER_USER:adm /etc/fail2ban-p2p > /dev/null
;;
install -d -o "$SERVER_USER" -g "$SERVER_GROUP" -m 0750 "$SERVER_HOME"
if [ -d "$CONFDIR" ]; then
chgrp "$SERVER_GROUP" "$CONFDIR" || true
chmod 0750 "$CONFDIR" || true
find "$CONFDIR" -type d -exec chmod 0750 {} \; || true
fi
if [ -f "$LOGFILE" ]; then
chown "$SERVER_USER":adm "$LOGFILE" || true
chmod 0640 "$LOGFILE" || true
fi
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
echo "postinst called with unknown argument '$1'" >&2
exit 1
;;
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

37
debian/postrm.ex vendored
View File

@@ -1,37 +0,0 @@
#!/bin/sh
# postrm script for fail2ban-p2p
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <postrm> `remove'
# * <postrm> `purge'
# * <old-postrm> `upgrade' <new-version>
# * <new-postrm> `failed-upgrade' <old-version>
# * <new-postrm> `abort-install'
# * <new-postrm> `abort-install' <old-version>
# * <new-postrm> `abort-upgrade' <old-version>
# * <disappearer's-postrm> `disappear' <overwriter>
# <overwriter-version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
purge|remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
;;
*)
echo "postrm called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

88
debian/preinst vendored Normal file → Executable file
View File

@@ -1,76 +1,44 @@
#!/bin/sh
# preinst script for fail2ban-p2p
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <new-preinst> `install'
# * <new-preinst> `install' <old-version>
# * <new-preinst> `upgrade' <old-version>
# * <old-preinst> `abort-upgrade' <new-version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
PACKAGE=fail2ban-p2p
SERVER_USER=${SERVER_USER:-fail2ban-p2p}
SERVER_GROUP=${SERVER_GROUP:-fail2ban-p2p}
SERVER_HOME=${SERVER_HOME:-/var/lib/fail2ban-p2p}
LOGFILE=/var/log/fail2ban-p2p.log
case "$1" in
install|upgrade)
# see http://www.debian.org/doc/manuals/securing-debian-howto/ch9.en.html
# Sane defaults:
[ -z "$SERVER_HOME" ] && SERVER_HOME="/var/run/fail2ban-p2p"
[ -z "$SERVER_USER" ] && SERVER_USER="fail2ban-p2p"
[ -z "$SERVER_NAME" ] && SERVER_NAME="Fail2ban-p2p user"
[ -z "$SERVER_GROUP" ] && SERVER_GROUP="fail2ban-p2p"
if ! getent group "$SERVER_GROUP" >/dev/null; then
addgroup --quiet --system "$SERVER_GROUP" || true
fi
# create user
# 1. create group if not existing
if ! getent group | grep -q "^$SERVER_GROUP:" ; then
echo -n "Adding group $SERVER_GROUP.."
addgroup --quiet --system $SERVER_GROUP 2>/dev/null ||true
echo "..done"
fi
# 2. create homedir if not existing
test -d $SERVER_HOME || mkdir $SERVER_HOME
# 3. create user if not existing
if ! getent passwd | grep -q "^$SERVER_USER:"; then
echo -n "Adding system user $SERVER_USER.."
adduser --quiet \
--system \
--ingroup $SERVER_GROUP \
--no-create-home \
--disabled-password \
$SERVER_USER 2>/dev/null || true
echo "..done"
fi
# 4. adjust passwd entry
usermod -c "$SERVER_NAME" \
-d $SERVER_HOME \
-g $SERVER_GROUP \
$SERVER_USER
# 5. adjust file and directory permissions
if ! dpkg-statoverride --list $SERVER_HOME >/dev/null
then
chown -R $SERVER_USER:adm $SERVER_HOME
chmod u=rwx,g=rxs,o= $SERVER_HOME
fi
# create logfile and make it owned by the user
test -f /var/log/fail2ban-p2p.log || touch /var/log/fail2ban-p2p.log
chown $SERVER_USER:adm /var/log/fail2ban-p2p.log > /dev/null
;;
if ! getent passwd "$SERVER_USER" >/dev/null; then
adduser --quiet \
--system \
--ingroup "$SERVER_GROUP" \
--home "$SERVER_HOME" \
--no-create-home \
--disabled-password \
--gecos "Fail2ban P2P service user" \
"$SERVER_USER" || true
fi
install -d -o "$SERVER_USER" -g "$SERVER_GROUP" -m 0750 "$SERVER_HOME"
install -d -o root -g "$SERVER_GROUP" -m 0750 /etc/fail2ban-p2p
install -d -o root -g adm -m 0755 /var/log
touch "$LOGFILE"
chown "$SERVER_USER":adm "$LOGFILE"
chmod 0640 "$LOGFILE"
;;
abort-upgrade)
;;
;;
*)
echo "preinst called with unknown argument \`$1'" >&2
echo "preinst called with unknown argument '$1'" >&2
exit 1
;;
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

38
debian/prerm.ex vendored
View File

@@ -1,38 +0,0 @@
#!/bin/sh
# prerm script for fail2ban-p2p
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <prerm> `remove'
# * <old-prerm> `upgrade' <new-version>
# * <new-prerm> `failed-upgrade' <old-version>
# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
# * <deconfigured's-prerm> `deconfigure' `in-favour'
# <package-being-installed> <version> `removing'
# <conflicting-package> <version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
remove|upgrade|deconfigure)
;;
failed-upgrade)
;;
*)
echo "prerm called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

70
debian/rules vendored
View File

@@ -1,65 +1,15 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Sample debian/rules that uses debhelper.
# This file was originally written by Joey Hess and Craig Small.
# As a special exception, when this file is copied by dh-make into a
# dh-make output file, you may use that output file without restriction.
# This special exception was added by Craig Small in version 0.37 of dh-make.
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
export PYBUILD_NAME=fail2ban-p2p
DESTDIR=$(CURDIR)/debian/fail2ban-p2p
%:
dh $@ --with python3
configure: configure-stamp
configure-stamp:
dh_testdir
touch configure-stamp
override_dh_auto_build:
python3 setup.py build
build:
clean: clean-inits
dh_testdir
dh_testroot
rm -f build-stamp configure-stamp
rm -rf build
# Does not hurt to ask distutils to do their duty
python setup.py clean
# Enforce removal of *.pyc files. Apparently dh_clean does
# not perform find on provided filename patterns.
find . -name \*.pyc -exec rm -f {} \;
dh_clean
install: build
dh_testdir
dh_testroot
dh_clean -k
dh_installdirs
# Install the package into debian/fail2ban-p2p.
python setup.py install --root=$(DESTDIR) --no-compile --install-layout=deb
#
# Just to comply with policy 4.8
binary-arch:
# Build architecture-independent files here.
binary-indep: install
dh_testdir
dh_testroot
#dh_installchangelogs ChangeLog
dh_installdocs
dh_installlogrotate
dh_pycentral
dh_installinit -- defaults 99
#dh_installman man/*.1
dh_link
dh_compress
dh_fixperms
dh_installdeb
dh_gencontrol
dh_md5sums
dh_builddeb
binary: binary-indep
.PHONY: build clean binary-indep binary-arch binary install configure copy-inits clean-inits
override_dh_auto_install:
python3 setup.py install \
--root=$(CURDIR)/debian/fail2ban-p2p \
--install-layout=deb \
--no-compile

4
debian/watch vendored Normal file
View File

@@ -0,0 +1,4 @@
version=4
opts="filenamemangle=s%(?:.*?)?v?([\d\.]+)\.tar\.gz%fail2ban-p2p-$1.tar.gz%" \
https://github.com/mmunz/fail2ban-p2p/tags \
(?:.*?/archive/refs/tags/)?v?([\d\.]+)\.tar\.gz

23
debian/watch.ex vendored
View File

@@ -1,23 +0,0 @@
# Example watch control file for uscan
# Rename this file to "watch" and then you can run the "uscan" command
# to check for upstream updates and more.
# See uscan(1) for format
# Compulsory line, this is a version 3 file
version=3
# Uncomment to examine a Webpage
# <Webpage URL> <string match>
#http://www.example.com/downloads.php fail2ban-p2p-(.*)\.tar\.gz
# Uncomment to examine a Webserver directory
#http://www.example.com/pub/fail2ban-p2p-(.*)\.tar\.gz
# Uncommment to examine a FTP server
#ftp://ftp.example.com/pub/fail2ban-p2p-(.*)\.tar\.gz debian uupdate
# Uncomment to find new files on sourceforge, for devscripts >= 2.9
# http://sf.net/fail2ban-p2p/fail2ban-p2p-(.*)\.tar\.gz
# Uncomment to find new files on GooglePages
# http://example.googlepages.com/foo.html fail2ban-p2p-(.*)\.tar\.gz

View File

@@ -1,43 +1,28 @@
#!/usr/bin/python2
#!/usr/bin/env python3
# Copyright 2013 Johannes Fuermann <johannes at fuermann.cc>
# Copyright 2013 Manuel Munz <manu at somakoma.de>
#
# This file is part of fail2ban-p2p.
#
# Licensed under the GNU GENERAL PUBLIC LICENSE Version 3. For details
# see the file COPYING or http://www.gnu.org/licenses/gpl-3.0.en.html.
"""
This script can be used to send a ban message to the own node.
To do this it will use the ip address and port given in the configfile
for this node.
"""
"""This script can be used to send a ban message to the own node."""
import argparse
import json
import os
import socket
import sys
from time import time
import M2Crypto
sys.path.insert(1, "./fail2ban-p2p")
sys.path.insert(2, "/usr/share/fail2ban-p2p/fail2ban-p2p")
import config
import hashlib
import os
import argparse
import crypto
import socket
import M2Crypto
from time import time
import json
import util
import version
# Parse arguments
parser = argparse.ArgumentParser(description='fail2ban-p2p-client help.')
parser.add_argument('-b', help='IP address to ban', metavar='IP')
parser.add_argument('-c', default='/etc/fail2ban-p2p/', help='Read configuration from DIR.',
metavar='DIR')
parser.add_argument('-d', help='Dump table of blocked hosts in the format <FORMAT> (table, json or count).',
metavar='FORMAT')
parser.add_argument('-t', help='The list of blocked hosts should go back that many seconds.',
metavar='SECONDS')
parser.add_argument('-c', default='/etc/fail2ban-p2p/', help='Read configuration from DIR.', metavar='DIR')
parser.add_argument('-d', help='Dump table of blocked hosts in the format <FORMAT> (table, json or count).', metavar='FORMAT')
parser.add_argument('-t', help='The list of blocked hosts should go back that many seconds.', metavar='SECONDS')
parser.add_argument('-q', action='store_true', help='Quiet, no output')
parser.add_argument('-v', action='store_true', help='Verbose output')
@@ -48,86 +33,77 @@ c.configPath = args.c or "/etc/fail2ban-p2p"
c.privkey = os.path.join(c.configPath, 'private.pem')
c.pubkey = os.path.join(c.configPath, 'public.pem')
if c.loadConfig() == False:
exit()
if c.loadConfig() is False:
sys.exit(1)
if not args.d and not args.b:
print "Please use the -b argument to specify an IP to ban or -d to request information about banned nodes."
exit()
print("Please use the -b argument to specify an IP to ban or -d to request information about banned nodes.")
sys.exit(1)
dump = False
if args.d:
dump = True;
if not args.d == "table" and not args.d == "json" and not args.d == "count":
dump = True
if args.d not in ("table", "json", "count"):
print("invalid value for -d argument!")
exit()
sys.exit(1)
timeframe = 3600
if args.t:
try:
timeframe = int(args.t)
except ValueError as e:
except ValueError:
print("Invalid Timeframe specified, only use integers! Using default value of 3600 instead")
timeframe = 3600
quiet = False
if args.q:
quiet = True
quiet = bool(args.q)
if dump:
# Generate a message of type 2 (request to dump list of banned hosts)
unordered_dict = {
"msgType": 2,
"parameter": { "TimeFrame": timeframe},
"hops": ['local']
"msgType": 2,
"parameter": {"TimeFrame": timeframe},
"hops": ['local'],
}
serializable_dict = util.sort_recursive(unordered_dict)
if args.b:
# Generate a ban message (Type 1)
serializable_dict = util.sort_recursive(unordered_dict)
elif args.b:
unordered_dict = {
"msgType": 1,
"parameter": { "Timestamp": int(time()), "AttackerIP": args.b, "Trustlevel": 100 },
"hops": ['local']
"msgType": 1,
"parameter": {"Timestamp": int(time()), "AttackerIP": args.b, "Trustlevel": 100},
"hops": ['local'],
}
serializable_dict = util.sort_recursive(unordered_dict)
serializable_dict = util.sort_recursive(unordered_dict)
if args.b or dump:
signed_message = json.dumps(serializable_dict)
signed_message = json.dumps(serializable_dict)
SignEVP = M2Crypto.EVP.load_key(c.privkey)
SignEVP.sign_init()
SignEVP.sign_update(signed_message)
StringSignature = SignEVP.sign_final().encode('hex')
signed_dict = {
#"protocolVersion": version.protocol,
"msg": serializable_dict,
"signature": StringSignature,
"protocolVersion": version.protocolVersion
}
cmdsigned = json.dumps(signed_dict)
signer = M2Crypto.EVP.load_key(c.privkey)
signer.sign_init()
signer.sign_update(signed_message.encode("utf-8"))
string_signature = signer.sign_final().hex()
signed_dict = {
"msg": serializable_dict,
"signature": string_signature,
"protocolVersion": version.protocolVersion,
}
cmdsigned = json.dumps(signed_dict)
ret = None
# send message
s = None
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(10)
s.connect((c.addresses[0], int(c.port)))
s.send(cmdsigned)
ret = s.recv(1048576) # we need about 50 Bytes per banned node
s.sendall(cmdsigned.encode("utf-8"))
ret = s.recv(1048576).decode("utf-8")
if not quiet and args.v:
print ("Message sent: " + cmdsigned)
except:
print("Message sent: " + cmdsigned)
except Exception:
if not quiet:
print ("could not connect to "+c.name+" ("+c.addresses[0]+":"+str(c.port)+")")
print("could not connect to " + c.name + " (" + c.addresses[0] + ":" + str(c.port) + ")")
finally:
s.close()
if s is not None:
s.close()
if ret:
if ret:
if args.d:
if "ERROR" in ret:
print("An error occured:\n")
@@ -144,7 +120,10 @@ if ret:
status = "PENDING"
if int(c.threshold) <= int(ban['Trustlevel']):
status = "BANNED"
print(ban['AttackerIP'].ljust(15, ' ') + "\t" + str(ban['Timestamp']) + "\t\t" + str(ban['BanTime']) + "\t\t" + str(ban['Trustlevel'])) + "\t\t" + status
print(
ban['AttackerIP'].ljust(15, ' ') + "\t" + str(ban['Timestamp']) + "\t\t" +
str(ban['BanTime']) + "\t\t" + str(ban['Trustlevel']) + "\t\t" + status
)
else:
print("No hosts in banlist")
else:

View File

@@ -1,31 +1,22 @@
#!/usr/bin/python2
#!/usr/bin/env python3
# Copyright 2013 Johannes Fuermann <johannes at fuermann.cc>
# Copyright 2013 Manuel Munz <manu at somakoma.de>
#
# This file is part of fail2ban-p2p.
#
# Licensed under the GNU GENERAL PUBLIC LICENSE Version 3. For details
# see the file COPYING or http://www.gnu.org/licenses/gpl-3.0.en.html.
# Main program for fail2ban-p2p.
# set lib paths
import argparse
import os
import sys
sys.path.insert(1, "./fail2ban-p2p")
sys.path.insert(2, "/usr/share/fail2ban-p2p/fail2ban-p2p")
import config
import node
import log
import os
import argparse
import crypto
import log
import node
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='fail2ban-p2p help.')
parser.add_argument('-K', action='store_true', help='Create private/public keypair')
parser.add_argument('-c', default='/etc/fail2ban-p2p/', help='Read configuration from DIR.',
metavar='DIR')
parser.add_argument('-c', default='/etc/fail2ban-p2p/', help='Read configuration from DIR.', metavar='DIR')
args = parser.parse_args()
c = config.Config()
@@ -33,15 +24,15 @@ if __name__ == "__main__":
c.privkey = os.path.join(c.configPath, 'private.pem')
c.pubkey = os.path.join(c.configPath, 'public.pem')
if c.loadConfig() == False:
raise OSError #, 'Config error, check log.'
if c.loadConfig() is False:
raise OSError('Config error, check log.')
logger = log.initialize_logging("fail2ban-p2p")
if args.K:
crypto.create_keys()
exit()
# make sure the keys exist
sys.exit(0)
if not os.path.isfile(c.privkey) or not os.path.isfile(c.pubkey):
logger.warning('Private or public key not found, creating them')
crypto.create_keys()
@@ -54,9 +45,9 @@ if __name__ == "__main__":
n.requestBanlist()
n.cleanBanlist()
n.openSocket()
except (KeyboardInterrupt):
except KeyboardInterrupt:
logger.info("Keyboard Interrupt received, going down")
n.cleanBanlistStop()
n.closeSocket()
if n is not None:
n.cleanBanlistStop()
n.closeSocket()
logger.info("kthxbai!")

View File

@@ -1,96 +1,60 @@
# Copyright 2013 Johannes Fuermann <johannes at fuermann.cc>
# Copyright 2013 Manuel Munz <manu at somakoma.de>
#
# This file is part of fail2ban-p2p.
#
# Licensed under the GNU GENERAL PUBLIC LICENSE Version 3. For details
# see the file COPYING or http://www.gnu.org/licenses/gpl-3.0.en.html.
import json
import util
import config
import log
import crypto
import M2Crypto
import json
import config
import log
import util
import version
logger = log.initialize_logging("fail2ban-p2p." + __name__)
class Command:
'''
Handle command objects.
Kwargs:
* protocolVersion (int): Protocol version number
* msgType (int): message type
* parameter (dict): Parameters to send in the message
* signature (string): The messages signature
* hops (array): Hops that previously have relayed this message
'''
"""Handle command objects."""
msgType = None
parameter = ()
signature = ""
hops = []
def __init__(self, protocolVersion=None, msgType=None, parameter={}, signature=None, hops=[]):
def __init__(self, protocolVersion=None, msgType=None, parameter=None, signature=None, hops=None):
self.msgType = msgType
self.parameter = parameter
self.parameter = parameter if parameter is not None else {}
self.protocolVersion = protocolVersion
self.signature = signature
self.hops = hops
self.hops = hops if hops is not None else []
def __string__(self):
return "Command (msgType = "+str(msgType)+", ...)"
return f"Command (msgType = {self.msgType}, ...)"
def toSerializableDict(self):
'''
Returns a recursively sorted dictionary
'''
unordered_dict = {
"msgType": self.msgType,
"parameter": self.parameter,
"hops": self.hops
"hops": self.hops,
}
return util.sort_recursive(unordered_dict)
def toProtocolMessage(self):
'''
Create a JSON-encoded Protocol message.
'''
serializable_dict = self.toSerializableDict()
signed_message = json.dumps(serializable_dict)
signature = self.sign(signed_message)
signed_dict = {
"msg": serializable_dict,
"signature": signature,
"protocolVersion": version.protocolVersion
"protocolVersion": version.protocolVersion,
}
return json.dumps(signed_dict)
def sign(self, text):
"""
Compute signature for a message.
Args:
text (string): the json encoded message text.
Returns:
A string with the signature for 'text'
"""
logger.debug("signing outgoing message")
c = config.Config()
SignEVP = M2Crypto.EVP.load_key(c.privkey)
SignEVP.sign_init()
SignEVP.sign_update(text)
StringSignature = SignEVP.sign_final().encode('hex')
logger.debug("Our signature for this message is: " + StringSignature)
self.signature = StringSignature
return StringSignature
signer = M2Crypto.EVP.load_key(c.privkey)
signer.sign_init()
signer.sign_update(text.encode("utf-8"))
string_signature = signer.sign_final().hex()
logger.debug("Our signature for this message is: %s", string_signature)
self.signature = string_signature
return string_signature

View File

@@ -1,15 +1,8 @@
# Copyright 2013 Johannes Fuermann <johannes at fuermann.cc>
# Copyright 2013 Manuel Munz <manu at somakoma.de>
#
# This file is part of fail2ban-p2p.
#
# Licensed under the GNU GENERAL PUBLIC LICENSE Version 3. For details
# see the file COPYING or http://www.gnu.org/licenses/gpl-3.0.en.html.
import ConfigParser
import configparser
import os
import logging
class Config:
"""
Handles config file loading and parsing of config values,
@@ -26,54 +19,45 @@ class Config:
port = 0
ownermail = ""
banTime = 0
threshold = 0
threshold = 0
def __init__(self, configPath = '/etc/fail2ban-p2p'):
self.__dict__ = self.__shared_state # borg pattern.
def __init__(self, configPath='/etc/fail2ban-p2p'):
self.__dict__ = self.__shared_state # borg pattern.
if configPath:
self.configPath = configPath
def loadConfig(self):
"""
Load and parse the config file
"""
def get_option(section, option, mandatory, default):
'''Gets an option from the config file
"""Load and parse the config file."""
Args:
section: Section in config file
option: option in config file
default: the default value if option was not found
mandatory: if this option is mandatory
Returns:
The value of the option we requested or the default value if the
option is not mandatory; else False
'''
def get_option(section, option, mandatory, default):
try:
value = config.get(section, option)
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
except (configparser.NoOptionError, configparser.NoSectionError):
if mandatory:
print "Mandatory option " + option + " not found in config file " + self.configFile
print(f"Mandatory option {option} not found in config file {self.configFile}")
return False
else:
value = default
value = default
return value
config = ConfigParser.RawConfigParser()
config = configparser.RawConfigParser()
self.configFile = os.path.join(self.configPath, 'fail2ban-p2p.conf')
if os.access(self.configFile, os.R_OK):
config.read(self.configFile)
self.logFile = get_option('Logging', 'logfile', False, '/var/log/fail2ban-p2p.log')
self.logLevel = get_option('Logging', 'loglevel', False, 'DEBUG')
self.logLevel = eval("logging." + self.logLevel) or 10
level_name = str(get_option('Logging', 'loglevel', False, 'DEBUG')).upper()
self.logLevel = getattr(logging, level_name, logging.DEBUG)
self.addresses = get_option('Node', 'addresses', False, '0.0.0.0')
self.addresses = [a.strip() for a in self.addresses.split(',')]
self.addresses = [a.strip() for a in str(self.addresses).split(',')]
self.name = get_option('Node', 'name', False, 'Ididnotsetaname')
self.port = get_option('Node', 'port', False, 1337)
self.banTime = get_option('Node', 'bantime', False, 7200)
self.ownermail = get_option('Node', 'ownermail', False, "ididnotsetmyemail@example.org")
self.threshold = get_option('Node', 'threshold', False, 80)
self.port = int(get_option('Node', 'port', False, 1337))
self.banTime = int(get_option('Node', 'bantime', False, 7200))
self.ownermail = get_option('Node', 'ownermail', False, 'ididnotsetmyemail@example.org')
self.threshold = int(get_option('Node', 'threshold', False, 80))
return True
else:
print('ERROR: Configuration directory "' + self.configPath + '" does not exist.\n' +
'Please create a configuration or specify another valid configuration directory with the "-c" argument.')
print(
'ERROR: Configuration directory "' + self.configPath + '" does not exist.\n'
'Please create a configuration or specify another valid configuration directory with the "-c" argument.'
)
return False

View File

@@ -1,43 +1,31 @@
# Copyright 2013 Johannes Fuermann <johannes at fuermann.cc>
# Copyright 2013 Manuel Munz <manu at somakoma.de>
#
# This file is part of fail2ban-p2p.
#
# Licensed under the GNU GENERAL PUBLIC LICENSE Version 3. For details
# see the file COPYING or http://www.gnu.org/licenses/gpl-3.0.en.html.
import os
import sys
import M2Crypto
import config
import os
import M2Crypto
import log
c = config.Config()
logger = log.initialize_logging("fail2ban-p2p." + __name__)
def create_keys():
"""Create private/public keypair (RSA 1024 bit)
If this function is called a private/public keypair is created if it does
not already exist. If the keypair already exists then the function will
ask for confirmation to overwrite it. The created keypair will be saved
in the config directory.
"""
"""Create private/public keypair (RSA 1024 bit)."""
if os.path.isfile(c.privkey) or os.path.isfile(c.pubkey):
print "A keypair for this node already exists."
ask = raw_input('Do you really want to create a new one? [y/N] ')
print("A keypair for this node already exists.")
ask = input('Do you really want to create a new one? [y/N] ')
if ask != "y":
return
M2Crypto.Rand.rand_seed (os.urandom (1024))
M2Crypto.Rand.rand_seed(os.urandom(1024))
logger.info("Generating a 1024 bit private/public key pair...")
keypair = M2Crypto.RSA.gen_key (1024, 65537)
keypair = M2Crypto.RSA.gen_key(1024, 65537)
try:
keypair.save_key(c.privkey, None)
os.chmod(c.privkey, 0400)
os.chmod(c.privkey, 0o400)
keypair.save_pub_key(c.pubkey)
logger.debug("Private key (secret) was saved to " + c.privkey)
logger.debug("Public key was saved to " + c.pubkey)
except IOError, e:
logger.error("Could not save the keypair, check permissions! " + "%s" % e)
exit()
logger.debug("Private key (secret) was saved to %s", c.privkey)
logger.debug("Public key was saved to %s", c.pubkey)
except IOError as e:
logger.error("Could not save the keypair, check permissions! %s", e)
sys.exit(1)

View File

@@ -1,30 +1,13 @@
# Copyright 2013 Johannes Fuermann <johannes at fuermann.cc>
# Copyright 2013 Manuel Munz <manu at somakoma.de>
#
# This file is part of fail2ban-p2p.
#
# Licensed under the GNU GENERAL PUBLIC LICENSE Version 3. For details
# see the file COPYING or http://www.gnu.org/licenses/gpl-3.0.en.html.
import command
import socket
import log
logger = log.initialize_logging("fail2ban-p2p." + __name__)
class Friend:
"""Contains information about friends (i.e. associated nodes).
"""Contains information about friends (i.e. associated nodes)."""
Kwargs:
* name (string): A name for this friend (derived from filename)
* uid (string): A unique identifier (sha224 of friends public key)
* address (array): IP Addresses or Domains where the friend is listening for
incoming connections
* port (int): Port for the friends listener (0-65535)
* publicKey (string): friends public key
* trustLevel (int): How much we trust messages from this friend (0-100%)
"""
def __init__(self, name="", uid="", address="", port=0, publicKey=0, trustLevel=0):
self.name = name
self.address = address
@@ -34,25 +17,19 @@ class Friend:
self.trustLevel = trustLevel
def sendCommand(self, command):
"""send a command message to a friend
Args:
* command -- Command object
"""
logger.debug("attempting to send a command to friend "+self.name)
"""Send a command message to a friend."""
logger.debug("attempting to send a command to friend %s", self.name)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
cmd = command.toProtocolMessage()
logger.debug("Message to be sent: "+cmd)
logger.debug("Message to be sent: %s", cmd)
try:
logger.debug("trying to connect to "+self.address+":"+str(self.port))
logger.debug("trying to connect to %s:%s", self.address, self.port)
s.settimeout(10)
s.connect((self.address, self.port))
s.send(cmd)
logger.debug ("Message sent: " + cmd)
except:
logger.warning("could not connect to friend "+self.name+" ("+self.address+":"+str(self.port)+")")
s.sendall(cmd.encode("utf-8"))
logger.debug("Message sent: %s", cmd)
except Exception:
logger.warning("could not connect to friend %s (%s:%s)", self.name, self.address, self.port)
finally:
s.close()

View File

@@ -1,39 +1,31 @@
# Copyright 2013 Johannes Fuermann <johannes at fuermann.cc>
# Copyright 2013 Manuel Munz <manu at somakoma.de>
#
# This file is part of fail2ban-p2p.
#
# Licensed under the GNU GENERAL PUBLIC LICENSE Version 3. For details
# see the file COPYING or http://www.gnu.org/licenses/gpl-3.0.en.html.
import config
import socket
import thread
import threading
from command import Command
import server
import log
import os
import re
import friend
import hashlib
import json
import version
import os
import re
import socket
import threading
from select import select
from time import time
import crypto
import M2Crypto
import customexceptions
import validators
import M2Crypto
import config
import crypto
import customexceptions
import friend
import log
import server
import validators
import version
from command import Command
logger = log.initialize_logging("fail2ban-p2p." + __name__)
class Node:
"""Handles the self-awareness of the program."""
__shared_state = {}
# config attributes
uid = 0
name = ""
addresses = []
@@ -41,7 +33,6 @@ class Node:
ownerMail = ""
banTime = 0
# working attributes
banList = []
messageQueue = []
friends = []
@@ -49,15 +40,11 @@ class Node:
lock = threading.Lock()
def __init__(self):
self.__dict__ = self.__shared_state # borg pattern.
self.__dict__ = self.__shared_state
def openSocket(self):
"""
Opens a server socket on the port specified in the config files, forks away a thread to
handle the incoming requests.
"""
logger.info("This is node " + str(self.name) + " (uid=" + str(self.uid) + ") coming up")
logger.debug("running version: " + version.version)
logger.info("This is node %s (uid=%s) coming up", self.name, self.uid)
logger.debug("running version: %s", version.version)
try:
sockets = []
for a in self.addresses:
@@ -67,48 +54,38 @@ class Node:
s.listen(1)
sockets.append(s)
except Exception as e:
logger.warning("Couldn't bind to address "+a+" (Reason: "+str(e)+")")
logger.warning("Couldn't bind to address %s (Reason: %s)", a, e)
while self.running:
readable, writable, errored = select(sockets, [], [])
readable, _, _ = select(sockets, [], [])
for sock in readable:
client_socket, address = sock.accept()
logger.debug("connection from "+address[0])
thread.start_new_thread(server.serve, (self, client_socket, address))
logger.debug("connection from %s", address[0])
t = threading.Thread(target=server.serve, args=(self, client_socket, address), daemon=True)
t.start()
except Exception as e:
print(e)
def closeSocket(self):
"""
Closes the server Socket
"""
logger.debug("closing socket")
self.running = False
def processMessages(self):
"""Locks own instance and acts on incoming messages."""
self.lock.acquire()
logger.debug("begin message handling")
for c in self.messageQueue:
if not self.uid in c.hops:
if self.uid not in c.hops:
if c.hops[0] == 'local':
del c.hops[0]
# relay if we're not already in the hops list
c.hops.append(self.uid)
if c.msgType == 1:
# apply friend's trustlevel
if not 'Trustlevel' in c.parameter:
logger.warn("Incoming Message has no Trustlevel, I won't trust it. Never.")
c.parameter['Trustlevel'] = 0;
if 'Trustlevel' not in c.parameter:
logger.warning("Incoming Message has no Trustlevel, I won't trust it. Never.")
c.parameter['Trustlevel'] = 0
if c.sender != "local":
c.parameter['Trustlevel'] = int((float(c.sender.trustLevel)/100 * float(c.parameter['Trustlevel'])/100)*100)
logger.debug("Message now has trust level "+ str(c.parameter['Trustlevel']))
# Aggregate trustlevel if that IP is already in our database under these conditions:
# * Timestamp of the received message has changed
# * The originator of this message did not send it before
c.parameter['Trustlevel'] = int((float(c.sender.trustLevel) / 100 * float(c.parameter['Trustlevel']) / 100) * 100)
logger.debug("Message now has trust level %s", c.parameter['Trustlevel'])
relay = True
ipindb = False
@@ -119,55 +96,61 @@ class Node:
logger.debug("IP already in database.")
if int(ban['Timestamp']) != int(c.parameter['Timestamp']):
if not c.hops[0] in ban['Hops']:
if c.hops[0] not in ban['Hops']:
trustold = ban['Trustlevel']
trustnew = int(trustold)+int(c.parameter['Trustlevel'])
trustnew = int(trustold) + int(c.parameter['Trustlevel'])
if trustnew > 100:
trustnew = 100
ban['Trustlevel'] = trustnew
ban['Hops'].append(c.hops[0])
logger.debug("TrustLevel for this IP is now "+str(trustnew))
logger.debug("TrustLevel for this IP is now %s", trustnew)
c.parameter['Trustlevel'] = trustnew
else:
relay = False
logger.debug("There is already an entry from %s in our database, do nothing with this message." % c.hops[0])
logger.debug("There is already an entry from %s in our database, do nothing with this message.", c.hops[0])
else:
relay = False
logger.debug("Timestamp has not changed, do nothing with this message")
if not ipindb:
self.banList.append({'AttackerIP': c.parameter['AttackerIP'], 'Timestamp': c.parameter['Timestamp'], 'BanTime': self.banTime, 'Trustlevel':c.parameter['Trustlevel'], 'Hops': [c.hops[0]]})
logger.debug("Added %s to internal banlist" % (c.parameter['AttackerIP']))
# write ban entry to log if the message's trust level is above our own threshold
self.banList.append({
'AttackerIP': c.parameter['AttackerIP'],
'Timestamp': c.parameter['Timestamp'],
'BanTime': self.banTime,
'Trustlevel': c.parameter['Trustlevel'],
'Hops': [c.hops[0]],
})
logger.debug("Added %s to internal banlist", c.parameter['AttackerIP'])
if relay:
if int(c.parameter['Trustlevel']) >= int(config.Config().threshold):
logger.ban(c.parameter['AttackerIP'])
else:
logger.debug("Message's trust level (%s) was below our threshold (%s)" % (c.parameter['Trustlevel'], config.Config().threshold))
logger.debug("Message's trust level (%s) was below our threshold (%s)", c.parameter['Trustlevel'], config.Config().threshold)
# Relay message
for friend in self.friends:
for peer in self.friends:
logger.debug("sending message to all friends")
friend.sendCommand(c)
peer.sendCommand(c)
if c.msgType == 3:
# dump all ips from banlist to the friend who send this dump request
sender_uid = c.hops[0]
for friend in self.friends:
logger.debug("Comparing senders uid (%s) with one of our friends uid (%s)" % (sender_uid, friend.uid))
if friend.uid == sender_uid:
logger.debug("The message is from our friend %s (uid: %s)" % ( friend.name, friend.uid) )
logger.debug("Dumping banlist to %s (uid: %s)" % ( friend.name, friend.uid) )
for peer in self.friends:
logger.debug("Comparing senders uid (%s) with one of our friends uid (%s)", sender_uid, peer.uid)
if peer.uid == sender_uid:
logger.debug("The message is from our friend %s (uid: %s)", peer.name, peer.uid)
logger.debug("Dumping banlist to %s (uid: %s)", peer.name, peer.uid)
if len(self.banList) > 0:
for ban in self.banList:
c = Command()
c.msgType = 1
c.hops = [ self.uid ]
c.protocolVersion = version.protocolVersion
c.parameter = { "AttackerIP": ban['AttackerIP'], "Timestamp": ban['Timestamp'], "Trustlevel": ban['Trustlevel'] }
friend.sendCommand(c)
cmd = Command()
cmd.msgType = 1
cmd.hops = [self.uid]
cmd.protocolVersion = version.protocolVersion
cmd.parameter = {
"AttackerIP": ban['AttackerIP'],
"Timestamp": ban['Timestamp'],
"Trustlevel": ban['Trustlevel'],
}
peer.sendCommand(cmd)
else:
logger.debug("I know this message, I won't resend it to prevent loops")
logger.debug("end message handling")
@@ -176,12 +159,6 @@ class Node:
self.lock.release()
def addMessage(self, command):
"""Locks Instance, adds message to queue, releases instanceLock
Args:
* command (obj): command object
"""
logger.debug("command added to queue")
self.lock.acquire()
self.messageQueue.append(command)
@@ -189,17 +166,17 @@ class Node:
self.processMessages()
def loadConfig(self):
"""Loads Config and own keypair."""
c = config.Config()
self.configPath = c.configPath
self.configFile = c.configFile
pubkey_file = open(c.pubkey, 'r').read()
pubkey = re.findall("-----BEGIN PUBLIC KEY-----(.*?)-----END PUBLIC KEY-----", pubkey_file, re.DOTALL|re.M)[0]
with open(c.pubkey, 'r', encoding='utf-8') as fh:
pubkey_file = fh.read()
pubkey = re.findall("-----BEGIN PUBLIC KEY-----(.*?)-----END PUBLIC KEY-----", pubkey_file, re.DOTALL | re.M)[0]
logger.debug("our own pubkey is: %s" % pubkey)
logger.debug("our own pubkey is: %s", pubkey)
self.uid = str(hashlib.sha224(pubkey).hexdigest())
self.uid = hashlib.sha224(pubkey.encode("utf-8")).hexdigest()
logger.debug("that makes our own uid: %s", self.uid)
self.addresses = c.addresses
self.port = c.port
@@ -208,210 +185,158 @@ class Node:
self.name = c.name
def getFriends(self):
"""Reads Friends from config path.
This iterates over all files in <config path>/friends, extracts all options and
add these friends to self.friends if their configuration is valid.
"""
error = False
friendPath = os.path.join(self.configPath, 'friends')
friendPath = os.path.join(self.configPath, 'friends')
friends = [f for f in os.listdir(friendPath) if os.path.isfile(os.path.join(friendPath, f))]
if not friends:
logger.warning("No friends found. In order to properly use fail2ban-p2p" +
" add at least one friend.")
if not friends:
logger.warning("No friends found. In order to properly use fail2ban-p2p add at least one friend.")
for file in friends:
with open(os.path.join(os.path.join(self.configPath, 'friends'), file), 'r') as f:
friendinfo = str(f.read())
f.closed
with open(os.path.join(friendPath, file), 'r', encoding='utf-8') as f:
friendinfo = f.read()
pubkey = None
try:
pubkey = re.findall("-----BEGIN PUBLIC KEY-----(.*?)-----END PUBLIC KEY-----", friendinfo, re.DOTALL|re.M)[0]
pubkey = re.findall("-----BEGIN PUBLIC KEY-----(.*?)-----END PUBLIC KEY-----", friendinfo, re.DOTALL | re.M)[0]
except IndexError:
logger.warning("No pubkey found in config for " + file)
logger.warning("No pubkey found in config for %s", file)
error = True
if pubkey:
logger.debug("read friend's public key: %s" % pubkey )
uid = str(hashlib.sha224(pubkey).hexdigest())
logger.debug("read friend's public key: %s", pubkey)
uid = hashlib.sha224(pubkey.encode("utf-8")).hexdigest()
try:
address = re.search("address\s*=\s*(.*)", friendinfo).group(1)
address = re.search(r"address\s*=\s*(.*)", friendinfo).group(1)
except AttributeError:
logger.warning("address not found in config for " + file)
logger.warning("address not found in config for %s", file)
error = True
try:
port = re.search("port\s*=\s*(.*)", friendinfo).group(1)
# make sure port is in valid range
port = re.search(r"port\s*=\s*(.*)", friendinfo).group(1)
if not 0 < int(port) < 65536:
logger.warning("Port is invalid in '%s' friend file, must be between 0 and 65535" % file)
logger.warning("Port is invalid in '%s' friend file, must be between 0 and 65535", file)
error = True
except AttributeError:
logger.warning("port not found in config for " + file)
logger.warning("port not found in config for %s", file)
error = True
try:
trustlevel = re.search("trustlevel\s*=\s*(.*)", friendinfo).group(1)
trustlevel = re.search(r"trustlevel\s*=\s*(.*)", friendinfo).group(1)
except AttributeError:
logger.warning("trustlevel not found in config for" + file)
logger.warning("trustlevel not found in config for %s", file)
error = True
if not error:
obj = friend.Friend(name=file, uid=uid, address=address, port=int(port),
trustLevel=int(trustlevel), publicKey=pubkey)
obj.configpath=os.path.join(os.path.join(self.configPath, 'friends'), file)
logger.debug("added friend " + file +
" (uid=" + uid + ", address=" + address + ", port=" + str(port) +
", trustLevel=" + str(trustlevel) + ")"
)
obj = friend.Friend(name=file, uid=uid, address=address, port=int(port), trustLevel=int(trustlevel), publicKey=pubkey)
obj.configpath = os.path.join(friendPath, file)
logger.debug("added friend %s (uid=%s, address=%s, port=%s, trustLevel=%s)", file, uid, address, port, trustlevel)
self.friends.append(obj)
else:
logger.error("Could not add friend '%s' due to errors in the config file" % file)
logger.error("Could not add friend '%s' due to errors in the config file", file)
error = False
def verifyMessage(self, message):
"""Verify a message
Args:
* message -- message object
"""
logger.debug("signature in command class is: "+str(message.signature))
logger.debug("signature in command class is: %s", message.signature)
logger.debug("attempting to verify command")
# semantic verification
# 1. Parameters for all msgTypes
# msgType
if not message.msgType:
logger.warn("Required parameter 'msgType' is missing in received message.")
if message.msgType is None:
logger.warning("Required parameter 'msgType' is missing in received message.")
raise customexceptions.InvalidMessage
if not validators.isInteger(message.msgType):
logger.warning("Invalid parameter 'msgType' in received message, can only be an integer.")
raise customexceptions.InvalidMessage
else:
if not validators.isInteger(message.msgType):
logger.warn("Invalid parameter 'msgType' in received message, can only be an integer.")
raise customexceptions.InvalidMessage
# Protocol version
if not version.protocolVersion == message.protocolVersion:
logger.warn("The protocol version of the received message (" + str(message.protocolVersion) + ") does not match the protocol version of this node (" + str(version.protocolVersion) + ").")
if version.protocolVersion != message.protocolVersion:
logger.warning(
"The protocol version of the received message (%s) does not match the protocol version of this node (%s).",
message.protocolVersion,
version.protocolVersion,
)
raise customexceptions.InvalidProtocolVersion
# Signature
if not message.signature:
logger.warn("Signature is missing in received message")
logger.warning("Signature is missing in received message")
raise customexceptions.InvalidMessage
# Hops
for h in message.hops:
if not validators.isAlphaNumeric(h):
logger.warn("Invalid characters in hops. Only alphanumeric characters are allowed.")
logger.warning("Invalid characters in hops. Only alphanumeric characters are allowed.")
raise customexceptions.InvalidMessage
# Parameters
if not message.parameter:
logger.warn("Message contains no parameters!")
logger.warning("Message contains no parameters!")
raise customexceptions.InvalidMessage
# 2. parameters for custom message types
if message.msgType == 1:
# Verify AttackerIP
if not ("AttackerIP" in message.parameter):
logger.warn("Required parameter 'AttackerIP' is missing in received message.")
if "AttackerIP" not in message.parameter:
logger.warning("Required parameter 'AttackerIP' is missing in received message.")
raise customexceptions.InvalidMessage
if not validators.isIPv4address(message.parameter['AttackerIP']):
logger.warning('Invalid parameter "AttackerIP" in received message.')
raise customexceptions.InvalidMessage
else:
if not validators.isIPv4address(message.parameter['AttackerIP']):
logger.warn('Invalid parameter "AttackerIP" in received message.')
raise customexceptions.InvalidMessage
# Verify Timestamp
if not ("Timestamp" in message.parameter):
logger.warn("Required parameter 'Timestamp' is missing in received message.")
if "Timestamp" not in message.parameter:
logger.warning("Required parameter 'Timestamp' is missing in received message.")
raise customexceptions.InvalidMessage
if not validators.isInteger(message.parameter['Timestamp']):
logger.warning('Invalid parameter "Timestamp" in received message.')
raise customexceptions.InvalidMessage
else:
if not validators.isInteger(message.parameter['Timestamp']):
logger.warn('Invalid parameter "Timestamp" in received message.')
raise customexceptions.InvalidMessage
# verify Trustlevel
if not 'Trustlevel' in message.parameter:
logger.warn('Required parameter "Trustlevel" in missing in received message.')
if 'Trustlevel' not in message.parameter:
logger.warning('Required parameter "Trustlevel" in missing in received message.')
raise customexceptions.InvalidMessage
if not (validators.isInteger(message.parameter['Trustlevel']) and 0 <= int(message.parameter['Trustlevel']) <= 100):
logger.warning('Invalid parameter "Trustlevel" in received message.')
raise customexceptions.InvalidMessage
else:
if not (validators.isInteger(message.parameter['Trustlevel']) and 0 <= int(message.parameter['Trustlevel']) <= 100):
logger.warn('Invalid parameter "Trustlevel" in received message.')
raise customexceptions.InvalidMessage
elif message.msgType == 2 or message.msgType == 3:
if not ("TimeFrame" in message.parameter):
logger.warn("Required parameter 'TimeFrame' is missing in received message.")
if "TimeFrame" not in message.parameter:
logger.warning("Required parameter 'TimeFrame' is missing in received message.")
raise customexceptions.InvalidMessage
if not validators.isInteger(message.parameter['TimeFrame']):
logger.warning('Invalid parameter "TimeFrame" in received message.')
raise customexceptions.InvalidMessage
else:
if not validators.isInteger(message.parameter['TimeFrame']):
logger.warn('Invalid parameter "TimeFrame" in received message.')
raise customexceptions.InvalidMessage
else:
logger.warn("Unknown message type: " + str(message.msgType))
logger.warning("Unknown message type: %s", message.msgType)
raise customexceptions.InvalidMessage
# signature
logger.debug("attempting to verify signature")
last_hop_uid = message.hops[len(message.hops)-1]
logger.debug("Last hop's uid is: %s" % ( last_hop_uid ) )
last_hop_uid = message.hops[len(message.hops) - 1]
logger.debug("Last hop's uid is: %s", last_hop_uid)
# look for known signatures
sender = None
for friend in self.friends:
logger.debug("Comparing last hops uid (%s) with one of our friends uid (%s)" % (last_hop_uid, friend.uid))
if friend.uid == last_hop_uid:
logger.debug("The message seems to be from our friend %s (uid: %s)" % ( friend.name, friend.uid) )
sender = friend
for peer in self.friends:
logger.debug("Comparing last hops uid (%s) with one of our friends uid (%s)", last_hop_uid, peer.uid)
if peer.uid == last_hop_uid:
logger.debug("The message seems to be from our friend %s (uid: %s)", peer.name, peer.uid)
sender = peer
pk = M2Crypto.RSA.load_pub_key(sender.configpath)
break
if last_hop_uid == "local": # the message was signed with our own key
if last_hop_uid == "local":
logger.debug("This message was signed with our own key.")
c = config.Config()
pk = M2Crypto.RSA.load_pub_key(c.pubkey)
sender = "local"
break
if sender == None:
if sender is None:
logger.warning("The message could not be mapped to one of our friends!")
raise customexceptions.InvalidSignature
# load sender's public key
VerifyEVP = M2Crypto.EVP.PKey()
# Assign the public key to our VerifyEVP
VerifyEVP.assign_rsa(pk)
# Begin verification
VerifyEVP.verify_init()
# verify the message against it
VerifyEVP.verify_update (json.dumps(message.toSerializableDict()))
verifier = M2Crypto.EVP.PKey()
verifier.assign_rsa(pk)
verifier.verify_init()
verifier.verify_update(json.dumps(message.toSerializableDict()).encode("utf-8"))
if(VerifyEVP.verify_final(message.signature.decode('hex')) != 1):
if verifier.verify_final(bytes.fromhex(message.signature)) != 1:
logger.warning('Signature doesnt match!')
return False
else:
logger.debug('Signature verified successfully')
message.sender = sender
return True
logger.debug('Signature verified successfully')
message.sender = sender
return True
def dumpBanlist(self, timeframe):
"""Generates List of Bans
Args:
* timeframe (int): Show nodes that were inserted this many seconds ago or later
Returns:
JSON encoded list of known bans.
"""
banlist = []
if not timeframe:
timeframe = 3600
timeframestart = int(time()) - int(timeframe)
logger.debug("Dumping all nodes that were inserted after " + str(timeframestart))
logger.debug("Dumping all nodes that were inserted after %s", timeframestart)
for entry in self.banList:
if int(entry['Timestamp']) > int(timeframestart):
banlist.append(entry)
@@ -419,31 +344,28 @@ class Node:
return json.dumps(banlist)
def requestBanlist(self):
"""Request a Ban List from all friends."""
for friend in self.friends:
logger.debug("Sending dump request to " + friend.name)
for peer in self.friends:
logger.debug("Sending dump request to %s", peer.name)
c = Command()
c.msgType = 3
c.hops = [ self.uid ]
c.hops = [self.uid]
c.protocolVersion = version.protocolVersion
c.parameter = { "TimeFrame": self.banTime or 3600 }
friend.sendCommand(c)
c.parameter = {"TimeFrame": self.banTime or 3600}
peer.sendCommand(c)
def cleanBanlist(self):
"""Purges expired bans, restarts itself after 60 seconds."""
logger.debug("Purging all entries from banlist that are older than %s seconds." % self.banTime)
logger.debug("Purging all entries from banlist that are older than %s seconds.", self.banTime)
if len(self.banList) > 0:
banListKeep = []
for ban in self.banList:
if ban['Timestamp'] + self.banTime > time():
banListKeep.append(ban)
else:
logger.info("Removed %s from internal banlist because the ban has expired." % ban['AttackerIP'])
logger.info("Removed %s from internal banlist because the ban has expired.", ban['AttackerIP'])
self.banList = banListKeep
global cleaner
cleaner = threading.Timer(60,self.cleanBanlist)
cleaner = threading.Timer(60, self.cleanBanlist)
cleaner.start()
def cleanBanlistStop(self):
"""stops cleanBanlist thread"""
cleaner.cancel()

View File

@@ -1,270 +1,3 @@
# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy.
# Passes Python2.7's test suite and incorporates all the latest updates.
from collections import OrderedDict
# This file is not GPLv3 like the other code but licensed under the MIT license:
# http://opensource.org/licenses/MIT
try:
from thread import get_ident as _get_ident
except ImportError:
from dummy_thread import get_ident as _get_ident
try:
from _abcoll import KeysView, ValuesView, ItemsView
except ImportError:
pass
class OrderedDict(dict):
"""
Dictionary that remembers insertion order
.. note::
This module was taken from http://code.activestate.com/recipes/576693/
and is not GPL but licensed under the MIT license.
"""
# An inherited dict maps keys to values.
# The inherited dict provides __getitem__, __len__, __contains__, and get.
# The remaining methods are order-aware.
# Big-O running times for all methods are the same as for regular dictionaries.
# The internal self.__map dictionary maps keys to links in a doubly linked list.
# The circular doubly linked list starts and ends with a sentinel element.
# The sentinel element never gets deleted (this simplifies the algorithm).
# Each link is stored as a list of length three: [PREV, NEXT, KEY].
def __init__(self, *args, **kwds):
'''Initialize an ordered dictionary. Signature is the same as for
regular dictionaries, but keyword arguments are not recommended
because their insertion order is arbitrary.
'''
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
try:
self.__root
except AttributeError:
self.__root = root = [] # sentinel node
root[:] = [root, root, None]
self.__map = {}
self.__update(*args, **kwds)
def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
'od.__setitem__(i, y) <==> od[i]=y'
# Setting a new item creates a new link which goes at the end of the linked
# list, and the inherited dictionary is updated with the new key/value pair.
if key not in self:
root = self.__root
last = root[0]
last[1] = root[0] = self.__map[key] = [last, root, key]
dict_setitem(self, key, value)
def __delitem__(self, key, dict_delitem=dict.__delitem__):
'od.__delitem__(y) <==> del od[y]'
# Deleting an existing item uses self.__map to find the link which is
# then removed by updating the links in the predecessor and successor nodes.
dict_delitem(self, key)
link_prev, link_next, key = self.__map.pop(key)
link_prev[1] = link_next
link_next[0] = link_prev
def __iter__(self):
'od.__iter__() <==> iter(od)'
root = self.__root
curr = root[1]
while curr is not root:
yield curr[2]
curr = curr[1]
def __reversed__(self):
'od.__reversed__() <==> reversed(od)'
root = self.__root
curr = root[0]
while curr is not root:
yield curr[2]
curr = curr[0]
def clear(self):
'od.clear() -> None. Remove all items from od.'
try:
for node in self.__map.itervalues():
del node[:]
root = self.__root
root[:] = [root, root, None]
self.__map.clear()
except AttributeError:
pass
dict.clear(self)
def popitem(self, last=True):
'''od.popitem() -> (k, v), return and remove a (key, value) pair.
Pairs are returned in LIFO order if last is true or FIFO order if false.
'''
if not self:
raise KeyError('dictionary is empty')
root = self.__root
if last:
link = root[0]
link_prev = link[0]
link_prev[1] = root
root[0] = link_prev
else:
link = root[1]
link_next = link[1]
root[1] = link_next
link_next[0] = root
key = link[2]
del self.__map[key]
value = dict.pop(self, key)
return key, value
# -- the following methods do not depend on the internal structure --
def keys(self):
'od.keys() -> list of keys in od'
return list(self)
def values(self):
'od.values() -> list of values in od'
return [self[key] for key in self]
def items(self):
'od.items() -> list of (key, value) pairs in od'
return [(key, self[key]) for key in self]
def iterkeys(self):
'od.iterkeys() -> an iterator over the keys in od'
return iter(self)
def itervalues(self):
'od.itervalues -> an iterator over the values in od'
for k in self:
yield self[k]
def iteritems(self):
'od.iteritems -> an iterator over the (key, value) items in od'
for k in self:
yield (k, self[k])
def update(*args, **kwds):
'''od.update(E, **F) -> None. Update od from dict/iterable E and F.
If E is a dict instance, does: for k in E: od[k] = E[k]
If E has a .keys() method, does: for k in E.keys(): od[k] = E[k]
Or if E is an iterable of items, does: for k, v in E: od[k] = v
In either case, this is followed by: for k, v in F.items(): od[k] = v
'''
if len(args) > 2:
raise TypeError('update() takes at most 2 positional '
'arguments (%d given)' % (len(args),))
elif not args:
raise TypeError('update() takes at least 1 argument (0 given)')
self = args[0]
# Make progressively weaker assumptions about "other"
other = ()
if len(args) == 2:
other = args[1]
if isinstance(other, dict):
for key in other:
self[key] = other[key]
elif hasattr(other, 'keys'):
for key in other.keys():
self[key] = other[key]
else:
for key, value in other:
self[key] = value
for key, value in kwds.items():
self[key] = value
__update = update # let subclasses override update without breaking __init__
__marker = object()
def pop(self, key, default=__marker):
'''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.
If key is not found, d is returned if given, otherwise KeyError is raised.
'''
if key in self:
result = self[key]
del self[key]
return result
if default is self.__marker:
raise KeyError(key)
return default
def setdefault(self, key, default=None):
'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
if key in self:
return self[key]
self[key] = default
return default
def __repr__(self, _repr_running={}):
'od.__repr__() <==> repr(od)'
call_key = id(self), _get_ident()
if call_key in _repr_running:
return '...'
_repr_running[call_key] = 1
try:
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, self.items())
finally:
del _repr_running[call_key]
def __reduce__(self):
'Return state information for pickling'
items = [[k, self[k]] for k in self]
inst_dict = vars(self).copy()
for k in vars(OrderedDict()):
inst_dict.pop(k, None)
if inst_dict:
return (self.__class__, (items,), inst_dict)
return self.__class__, (items,)
def copy(self):
'od.copy() -> a shallow copy of od'
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
'''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
and values equal to v (which defaults to None).
'''
d = cls()
for key in iterable:
d[key] = value
return d
def __eq__(self, other):
'''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
while comparison to a regular mapping is order-insensitive.
'''
if isinstance(other, OrderedDict):
return len(self)==len(other) and self.items() == other.items()
return dict.__eq__(self, other)
def __ne__(self, other):
return not self == other
# -- the following methods are only used in Python 2.7 --
def viewkeys(self):
"od.viewkeys() -> a set-like object providing a view on od's keys"
return KeysView(self)
def viewvalues(self):
"od.viewvalues() -> an object providing a view on od's values"
return ValuesView(self)
def viewitems(self):
"od.viewitems() -> a set-like object providing a view on od's items"
return ItemsView(self)
__all__ = ["OrderedDict"]

View File

@@ -1,65 +1,37 @@
# Copyright 2013 Johannes Fuermann <johannes at fuermann.cc>
# Copyright 2013 Manuel Munz <manu at somakoma.de>
#
# This file is part of fail2ban-p2p.
#
# Licensed under the GNU GENERAL PUBLIC LICENSE Version 3. For details
# see the file COPYING or http://www.gnu.org/licenses/gpl-3.0.en.html.
import command
import re
import log
import pdb
import json
# constants for length of RELEASE and MSGTYPE
import command
import log
logger = log.initialize_logging("fail2ban-p2p." + __name__)
def getDictValue(dict, key):
"""Get a value from a dict.
Args:
* dict: The dictionary to get the value from
* key: Key of the value to fetch
Returns:
Corresponding value if the key exists or False.
"""
def getDictValue(data, key):
try:
return dict[key]
except KeyError, e:
return data[key]
except KeyError:
return False
def parse(msg):
"""Parse a protocol message to a command object.
Args:
* msg -- the received message string
Returns:
command object
"""
"""Parse a protocol message to a command object."""
logger.debug("parsing message...")
signed_dict = False
if isinstance(msg, bytes):
msg = msg.decode("utf-8")
try:
signed_dict = json.loads(msg)
except ValueError, e:
except ValueError:
logger.warning("The received message does not appear to be valid json.")
return False
if signed_dict:
message_dict = getDictValue(signed_dict, 'msg')
msg = command.Command()
message_dict = getDictValue(signed_dict, 'msg')
msg_obj = command.Command()
msg.signature = getDictValue(signed_dict, 'signature')
msg.protocolVersion = getDictValue(signed_dict, 'protocolVersion')
msg.msgType = getDictValue(message_dict, 'msgType')
msg.parameter = getDictValue(message_dict, 'parameter')
msg.hops = getDictValue(message_dict, 'hops')
return msg
msg_obj.signature = getDictValue(signed_dict, 'signature')
msg_obj.protocolVersion = getDictValue(signed_dict, 'protocolVersion')
msg_obj.msgType = getDictValue(message_dict, 'msgType')
msg_obj.parameter = getDictValue(message_dict, 'parameter')
msg_obj.hops = getDictValue(message_dict, 'hops')
return msg_obj

View File

@@ -1,31 +1,14 @@
# Copyright 2013 Johannes Fuermann <johannes at fuermann.cc>
# Copyright 2013 Manuel Munz <manu at somakoma.de>
#
# This file is part of fail2ban-p2p.
#
# Licensed under the GNU GENERAL PUBLIC LICENSE Version 3. For details
# see the file COPYING or http://www.gnu.org/licenses/gpl-3.0.en.html.
from parser import parse
import log
import customexceptions
import log
logger = log.initialize_logging("fail2ban-p2p." + __name__)
def serve(n, connection, address):
"""Starts the server listener socket to receive messages
Args:
* n -- node object
* connection -- client socket
* address -- IP address of the node that sent the message
"""
"""Starts the server listener socket to receive messages."""
data = connection.recv(1024)
logger.debug("Parsing message: " + data)
logger.debug("Parsing message: %s", data)
command = parse(data)
@@ -37,30 +20,21 @@ def serve(n, connection, address):
n.addMessage(command)
if command.msgType == 2:
timeframe = int(command.parameter['TimeFrame'])
logger.debug("Requested Timeframe is: " + str(timeframe))
connection.send(n.dumpBanlist(timeframe))
logger.debug("Requested Timeframe is: %s", timeframe)
connection.sendall(n.dumpBanlist(timeframe).encode("utf-8"))
else:
connection.send("OK\n")
connection.sendall(b"OK\n")
except customexceptions.InvalidMessage, e:
connection.send("ERROR Invalid message\n")
logger.warn("This message made no sense.")
except customexceptions.InvalidSignature, e:
connection.send("ERROR invalid signature\n")
logger.warn("The Signature could not be verified")
except customexceptions.InvalidProtocolVersion, e:
connection.send("ERROR invalid protocol version\n")
except customexceptions.InvalidMessage:
connection.sendall(b"ERROR Invalid message\n")
logger.warning("This message made no sense.")
except customexceptions.InvalidSignature:
connection.sendall(b"ERROR invalid signature\n")
logger.warning("The Signature could not be verified")
except customexceptions.InvalidProtocolVersion:
connection.sendall(b"ERROR invalid protocol version\n")
else:
connection.send("Error\n")
logger.warn('invalid message')
connection.sendall(b"Error\n")
logger.warning('invalid message')
connection.close()
#except Exception, e:
#logger.warn("During the validation of the received message the " +
#"exception \"%s\" occured" % (type(e),))
#logger.debug("The received command was: " + data)
#connection.send("ERROR\n")
#finally:
#connection.close()

View File

@@ -1,41 +1,15 @@
# Copyright 2013 Johannes Fuermann <johannes at fuermann.cc>
# Copyright 2013 Manuel Munz <manu at somakoma.de>
#
# This file is part of fail2ban-p2p.
#
# Licensed under the GNU GENERAL PUBLIC LICENSE Version 3. For details
# see the file COPYING or http://www.gnu.org/licenses/gpl-3.0.en.html.
from collections import OrderedDict
from odict import OrderedDict
def sort_recursive(dictionary):
"""
Recursively sorts nested dictionaries. Should not be applied if the
structures are nested too deeply and/or there is even the remote
possibility that the nesting of the passed dictionary contains a cyclic
structure.
Args:
dictionary (dict): A python dictionary
Returns:
A recursively sorted dictionary
Example:
>>> dict = { 'a': '2', 'c': 3, 'b': { 'e': 4, 'd': 1 }, 'f': 5}
>>> sort_recursive(dict)
OrderedDict([('a', '2'), ('b', OrderedDict([('d', 1), ('e', 4)])), ('c', 3), ('f', 5)])
"""
sorted_list = OrderedDict(sorted(dictionary.items(), key = lambda x: x[0]))
# TODO test for cyclic structures.
"""Recursively sorts nested dictionaries."""
sorted_list = OrderedDict(sorted(dictionary.items(), key=lambda x: x[0]))
for key, value in sorted_list.items():
if type(value) is dict:
if isinstance(value, dict):
sorted_list[key] = sort_recursive(value)
return sorted_list
if __name__ == '__main__':
import doctest
doctest.testmod()

View File

@@ -1,63 +1,38 @@
"""
Copyright 2013 Johannes Fuermann <johannes at fuermann.cc>
Copyright 2013 Manuel Munz <manu at somakoma.de>
"""Setup script for fail2ban-p2p."""
This file is part of fail2ban-p2p.
Licensed under the GNU GENERAL PUBLIC LICENSE Version 3. For details
see the file COPYING or http://www.gnu.org/licenses/gpl-3.0.en.html.
"""
"""
This script can be used to send a ban message to the own node.
To do this it will use the ip address and port given in the configfile
for this node.
"""
from distutils.core import setup
from os.path import isfile, join, isdir
import sys
from sys import argv
from glob import glob
import sys
from setuptools import setup
sys.path.insert(1, "./fail2ban-p2p")
from version import version
longdesc = '''
Fail2Ban-P2P can be used to exchange information
about attackers between different hosts that are
running fail2ban in a P2P/F2F network.
Fail2Ban-P2P can be used to exchange fail2ban attacker info between hosts
that are running fail2ban in a P2P/F2F network.
'''
setup(
name = "fail2ban-p2p",
#version = version,
description = "exchange fail2ban attacker info between hosts using P2P",
long_description = longdesc,
version = version,
author = "Johannes Fuermann, Manuel Munz",
author_email = "foo@bar.xyz",
url = "https://svn.physik.uni-augsburg.de/projects/fail2ban-p2p",
license = "GPL",
platforms = "Posix",
scripts = [
'fail2ban-p2p.py',
'fail2ban-p2p-client.py'
],
packages = [
'fail2ban-p2p'
],
data_files = [
name="fail2ban-p2p",
description="exchange fail2ban attacker info between hosts using P2P",
long_description=longdesc,
version=version,
author="Johannes Fuermann, Manuel Munz",
author_email="foo@bar.xyz",
url="https://github.com/mmunz/fail2ban-p2p",
license="GPL",
platforms="Posix",
scripts=['fail2ban-p2p.py', 'fail2ban-p2p-client.py'],
packages=['fail2ban-p2p'],
data_files=[
('/etc/fail2ban-p2p', glob("config/*.conf")),
('/etc/fail2ban-p2p/friends', glob('config/friends/*'))
]
('/etc/fail2ban-p2p/friends', glob('config/friends/*')),
],
)
# Update config file
if argv[1] == "install":
print
print "Please do not forget to update your configuration files."
print "They are in /etc/fail2ban-p2p/."
print
if len(sys.argv) > 1 and sys.argv[1] == "install":
print()
print("Please do not forget to update your configuration files.")
print("They are in /etc/fail2ban-p2p/.")
print()