main
HerrHase 2 weeks ago
parent 82a40dc503
commit 71683e86eb

3
.gitignore vendored

@ -0,0 +1,3 @@
# ---> Ansible
*.retry

@ -0,0 +1,24 @@
common_development: false
# common_fail2ban.conf
common_fail2ban_loglevel: INFO
common_fail2ban_logtarget: /var/log/common_fail2ban.log
common_fail2ban_syslog_socket: auto
common_fail2ban_dbpurgeage: 1d
common_fail2ban_dbmaxmatches: 10
# jail.conf
common_fail2ban_ignoreself: "true"
common_fail2ban_ignoreips:
- 127.0.0.1/8
- ::1
common_fail2ban_findtime: 600
common_fail2ban_maxretry: 5
common_fail2ban_bantime: 600
common_fail2ban_protocol: "tcp"
common_fail2ban_jails:
- ssh.conf

@ -0,0 +1,12 @@
#
# handlers: main
#
#
# @author Björn Hase <herrhase@node001.net>
#
- name: restart fail2ban
service:
name: fail2ban
enabled: yes
state: restarted

@ -0,0 +1,17 @@
#
# playbook: configure
#
#
# @author Björn Hase <herrhase@node001.net>
#
- name: Install
hosts: all
become: yes
roles:
- common
tasks:
- include_role:
name: common
tasks_from: configure-fail2ban.yml

@ -0,0 +1,21 @@
#
# playbook: install basic stuff
#
#
# @author Björn Hase <herrhase@node001.net>
#
- name: Install
hosts: all
become: yes
roles:
- common
tasks:
- include_role:
name: common
tasks_from: install.yml
- include_role:
name: common
tasks_from: deployer.yml

@ -0,0 +1,18 @@
#
# playbook: update
#
#
# @author Björn Hase <herrhase@node001.net>
#
- name: Update Server
hosts: all
become: yes
roles:
- common
tasks:
- include_role:
name: common
tasks_from: update.yml

@ -0,0 +1,21 @@
# Generic configuration file for -botsearch filters
#
# @source https://scalastic.io/en/ufw-fail2ban-nginx/#step-4-configuring-fail2bans-basic-rules
#
[Init]
# Block is the actual non-found directories to block
block = \/?(<webmail>|<phpmyadmin>|<wordpress>|cgi-bin|mysqladmin)[^,]*
# These are just convenient definitions that assist the blocking of stuff that
# isn't installed
webmail = roundcube|(ext)?mail|horde|(v-?)?webmail
phpmyadmin = (typo3/|xampp/|admin/|)(pma|(php)?[Mm]y[Aa]dmin)
wordpress = wp-(login|signup|admin)\.php
# DEV Notes:
# Taken from apache-botsearch filter
#
# Author: Frantisek Sumsal

@ -0,0 +1,9 @@
# block
#
# @source https://scalastic.io/en/ufw-fail2ban-nginx/#step-4-configuring-fail2bans-basic-rules
#
[Definition]
failregex = ^<HOST>.*"(GET|PUT|POST).*" (403) .*$
ignoreregex = .*(robots.txt|favicon.ico|css|htc|js|asf|asx|wax|wmv|wmx|avi|bmp|class|divx|doc|docx|eot|exe|gif|gz|gzip|ico|jpg|jpeg|jpe|json|mdb|mid|midi|mov|qt|mp3|m4a|mp4|m4v|mpeg|mpg|mpe|mpp|otf|odb|odc|odf|odg|odp|ods|odt|ogg|pdf|png|pot|pps|ppt|pptx|ra|ram|svg|svgz|swf|tar|tif|tiff|ttf|ttc|wav|wma|wri|xla|xls|xlsx|xlt|xlw|zip)

@ -0,0 +1,20 @@
# Fail2Ban filter to match web requests for selected URLs that don't exist
# Based on apache-botsearch filter
#
# Author: Frantisek Sumsal
#
# @source https://scalastic.io/en/ufw-fail2ban-nginx/#step-4-configuring-fail2bans-basic-rules
[INCLUDES]
# Load regexes for filtering
before = botsearch-common.conf
[Definition]
failregex = ^<HOST> \- \S+ \[\] \"(GET|POST|HEAD) \/<block> \S+\" 404 .+$
^ \[error\] \d+#\d+: \*\d+ (\S+ )?\"\S+\" (failed|is not found) \(2\: No such file or directory\), client\: <HOST>\, server\: \S*\, request: \"(GET|POST|HEAD) \/<block> \S+\"\, .*?$
ignoreregex =
datepattern = {^LN-BEG}%%ExY(?P<_sep>[-/.])%%m(?P=_sep)%%d[T ]%%H:%%M:%%S(?:[.,]%%f)?(?:\s*%%z)?
^[^\[]*\[({DATE})
{^LN-BEG}

@ -0,0 +1,31 @@
# Generic nginx error_log configuration items (to be used as interpolations) in other
# filters monitoring nginx error-logs
#
# @source https://scalastic.io/en/ufw-fail2ban-nginx/#step-4-configuring-fail2bans-basic-rules
[DEFAULT]
# Type of log-file resp. log-format (file, short, journal):
logtype = file
# Daemon definition is to be specialized (if needed) in .conf file
_daemon = nginx
# Common line prefixes (beginnings) which could be used in filters
#
# [bsdverbose]? [hostname] [vserver tag] daemon_id spaces
#
# This can be optional (for instance if we match named native log files)
__prefix = <lt_<logtype>/__prefix>
__err_type = error
__prefix_line = %(__prefix)s\[%(__err_type)s\] \d+#\d+: \*\d+\s+
[lt_file]
__prefix = \s*
[lt_short]
__prefix = \s*(?:(?!\[)\S+ %(_daemon)s\[\d+\]: [^\[]*)?
[lt_journal]
__prefix = %(lt_short/__prefix)s

@ -0,0 +1,9 @@
# targets attempts to access forbidden directories
#
# @source https://scalastic.io/en/ufw-fail2ban-nginx/#step-4-configuring-fail2bans-basic-rules
#
[Definition]
failregex = directory index of .+ is forbidden, client: <HOST>, server: .+
ignoreregex =

@ -0,0 +1,44 @@
# Blocking auth
#
# @source https://github.com/fail2ban/fail2ban/blob/master/config/filter.d/nginx-http-auth.conf
#
# fail2ban filter configuration for nginx
[INCLUDES]
before = nginx-error-common.conf
[Definition]
mode = normal
__err_type = <_ertp-<mode>>
_ertp-auth = error
mdre-auth = ^%(__prefix_line)suser "(?:[^"]+|.*?)":? (?:password mismatch|was not found in "[^\"]*"), client: <HOST>, server: \S*, request: "\S+ \S+ HTTP/\d+\.\d+", host: "\S+"(?:, referrer: "\S+")?\s*$
_ertp-fallback = crit
mdre-fallback = ^%(__prefix_line)sSSL_do_handshake\(\) failed \(SSL: error:\S+(?: \S+){1,3} too (?:long|short)\)[^,]*, client: <HOST>
_ertp-normal = %(_ertp-auth)s
mdre-normal = %(mdre-auth)s
_ertp-aggressive = (?:%(_ertp-auth)s|%(_ertp-fallback)s)
mdre-aggressive = %(mdre-auth)s
%(mdre-fallback)s
failregex = <mdre-<mode>>
ignoreregex =
datepattern = {^LN-BEG}
journalmatch = _SYSTEMD_UNIT=nginx.service + _COMM=nginx
# DEV NOTES:
# mdre-auth:
# Based on samples in https://github.com/fail2ban/fail2ban/pull/43/files
# Extensive search of all nginx auth failures not done yet.
#
# Author: Daniel Black
# mdre-fallback:
# Ban people checking for TLS_FALLBACK_SCSV repeatedly
# https://stackoverflow.com/questions/28010492/nginx-critical-error-with-ssl-handshaking/28010608#28010608
# Author: Stephan Orlowsky

@ -0,0 +1,46 @@
# Fail2ban filter configuration for nginx :: limit_req
# used to ban hosts, that were failed through nginx by limit request processing rate
#
# Author: Serg G. Brester (sebres)
#
# To use 'nginx-limit-req' filter you should have `ngx_http_limit_req_module`
# and define `limit_req` and `limit_req_zone` as described in nginx documentation
# http://nginx.org/en/docs/http/ngx_http_limit_req_module.html
#
# Example:
#
# http {
# ...
# limit_req_zone $binary_remote_addr zone=lr_zone:10m rate=1r/s;
# ...
# # http, server, or location:
# location ... {
# limit_req zone=lr_zone burst=1 nodelay;
# ...
# }
# ...
# }
# ...
#
[Definition]
# Specify following expression to define exact zones, if you want to ban IPs limited
# from specified zones only.
# Example:
#
# ngx_limit_req_zones = lr_zone|lr_zone2
#
ngx_limit_req_zones = [^"]+
# Use following full expression if you should range limit request to specified
# servers, requests, referrers etc. only :
#
# failregex = ^\s*\[[a-z]+\] \d+#\d+: \*\d+ limiting requests, excess: [\d\.]+ by zone "(?:%(ngx_limit_req_zones)s)", client: <HOST>, server: \S*, request: "\S+ \S+ HTTP/\d+\.\d+", host: "\S+"(, referrer: "\S+")?\s*$
# Shortly, much faster and stable version of regexp:
failregex = ^\s*\[[a-z]+\] \d+#\d+: \*\d+ limiting requests, excess: [\d\.]+ by zone "(?:%(ngx_limit_req_zones)s)", client: <HOST>,
ignoreregex =
datepattern = {^LN-BEG}

@ -0,0 +1,19 @@
#
# nginx-noscript
#
#
[INCLUDES]
# overwrite with apache-common.local if _apache_error_client is incorrect.
before = apache-common.conf
[Definition]
script = /\S*(?:php(?:[45]|[.-]cgi)?|\.asp|\.exe|\.pl|\bcgi-bin/)
prefregex = ^%(_apache_error_client)s (?:AH0(?:01(?:28|30)|1(?:264|071)|2811): )?(?:(?:[Ff]ile|script|[Gg]ot) )<F-CONTENT>.+</F-CONTENT>$
failregex = ^(?:does not exist|not found or unable to stat): <script>\b
^'<script>\S*' not found or unable to stat
\[error\].+"[Pp]rimary script unknown"(?:\\n)?

@ -0,0 +1,11 @@
# protects against SSL handshake failure attacks
#
#
[Definition]
failregex = SSL_do_handshake\(\) failed .+ while SSL handshaking, client: <HOST>, server: .+
ignoreregex =
datepattern = {^LN-BEG}%%ExY(?P<_sep>[-/.])%%m(?P=_sep)%%d[T ]%%H:%%M:%%S(?:[.,]%%f)?(?:\s*%%z)?
^[^\[]*\[({DATE})
{^LN-BEG}

@ -0,0 +1,8 @@
# wordpress login failed
[INCLUDES]
before = common.conf
[Definition]
failregex = <HOST>.*POST.*(wp-login\.php|xmlrpc\.php).* 200
ignoreregex =

@ -0,0 +1,4 @@
# wordpress cron
[Definition]
failregex = ^<HOST> .*POST .*wp-cron\.php.*

@ -0,0 +1,8 @@
# wordpress xmlrpc
[INCLUDES]
before = common.conf
[Definition]
failregex = <HOST>.*(xmlrpc\.php).* 403
ignoreregex =

@ -0,0 +1,8 @@
[mysqld-auth]
enabled = true
port = 3306
filter = mysqld-auth
logpath = /var/log/mysql/error.log
findtime = 3600
maxretry = 5
bantime = 86400

@ -0,0 +1,9 @@
# stop some known malicious bot request patterns
[nginx-badbots]
enabled = true
port = http,https
filter = apache-badbots
logpath = /var/log/nginx/*access.log
maxretry = 2

@ -0,0 +1,7 @@
[nginx-botsearch]
enabled = true
port = http,https
filter = nginx-botsearch
logpath = /var/log/nginx/*access.log
maxretry = 2

@ -0,0 +1,9 @@
# ban clients that are searching for scripts on the website to execute and exploit,
# only using if php not in use
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/*error.log

@ -0,0 +1,10 @@
# dos
[nginx-req-limit]
enabled = true
filter = nginx-limit-req
logpath = /var/log/nginx/*error.log
findtime = 600
bantime = 7200
maxretry = 10

@ -0,0 +1,7 @@
[nginx-nohome]
enabled = true
port = http,https
filter = apache-nohome
logpath = /var/log/nginx/*access.log
maxretry = 2

@ -0,0 +1,7 @@
[nginx-noscript]
enabled = true
port = http,https
filter = nginx-noscript
logpath = /var/log/nginx/*error.log
maxretry = 2

@ -0,0 +1,5 @@
[ssh]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log

@ -0,0 +1,7 @@
[wordpress-auth]
enabled = true
filter = wordpress-auth
logpath = /var/log/nginx/*access.log
maxretry = 5
bantime = 1 day
datepattern = %%d/%%b/%%Y:%%H:%%M:%%S \+%%f

@ -0,0 +1,7 @@
[wordpress-cron]
enabled = true
filter = wordpress-cron
logpath = /var/log/nginx/*access.log
maxretry = 5
bantime = 1 day
datepattern = %%d/%%b/%%Y:%%H:%%M:%%S \+%%f

@ -0,0 +1,7 @@
[wordpress-xmlrpc]
enabled = true
filter = wordpress-xmlrpc
logpath = /var/log/nginx/*access.log
maxretry = 1
bantime = 1 day
datepattern = %%d/%%b/%%Y:%%H:%%M:%%S \+%%f

@ -0,0 +1,48 @@
#
# task: configure fail2ban
#
# @author Björn Hase <herrhase@node001.net>
#
- name: adding configuration fail2ban/fail2ban.local
template:
src: fail2ban/fail2ban.local
dest: /etc/fail2ban/fail2ban.local
owner: root
group: root
mode: '0644'
- name: adding configuration fail2ban/jail.local
template:
src: fail2ban/jail.local
dest: /etc/fail2ban/jail.local
owner: root
group: root
mode: '0644'
- name: copy filters
copy:
src: ../resources/fail2ban/filters/
dest: /etc/fail2ban/filter.d/
owner: root
group: root
mode: '0644'
- name: find & delete old jails files
find:
paths: /etc/fail2ban/jail.d
register: fail2ban_outdated_files
- file:
state: absent
path: "{{ item.path }}"
loop: "{{ fail2ban_outdated_files.files }}"
when: fail2ban_outdated_files.files is defined
- name: enable jails
copy:
src: ../ressources/fail2ban/jail/{{ item }}
dest: /etc/fail2ban/jail.d/{{ item }}
with_items: "{{ common_fail2ban_jails }}"
- notify: restart fail2ban

@ -0,0 +1,18 @@
#
# fail2ban
# adding ssh jail
#
#
# ufw
# only allows ssh
#
- name: Configure ufw
ufw:
rule: allow
name: OpenSSH
- name: Enable ufw
ufw:
state: enabled

@ -0,0 +1,42 @@
#
# task: create deployer
#
#
# @author Björn Hase <herrhase@node001.net>
#
- name: Check if users exist
getent:
database: passwd
key: deploy
fail_key: false
- name: create password
set_fact:
password="{{ lookup('ansible.builtin.password', '~/deploy.txt', length=30, chars=['ascii_letters', 'digits']) }}"
#
# create user if not exists
#
- name: create user
user:
name: deploy
password: "{{ password }}"
create_home: yes
when: ansible_facts.getent_passwd|bool == false
#
# create ssh-key add to authorized_keys as deploy user
#
- stat:
path: ~/.ssh/git
become_user: deploy
register: git_key
- name: create ssh
shell: |
ssh-keygen -t ed25519 -f ~/.ssh/git -C $(hostname) -q -P ""
cat ~/.ssh/git.pub >> ~/.ssh/authorized_keys
become_user: deploy
when: ansible_facts.getent_passwd|bool == false and not git_key.stat.exists

@ -0,0 +1,30 @@
#
# task: install
#
- name: Remove ubuntu tools
apt:
name: ubuntu-advantage-tools
state: absent
#
# install software packages
#
- name: Install unzip, curl, software-properties-common, git & ca-certificates gnupg
apt:
name:
- unzip
- curl
- software-properties-common
- ca-certificates
- gnupg
- git
- ufw
- fail2ban
- sshpass
- python3
- python3-pip
- python3-passlib
update_cache: yes
cache_valid_time: 3600

@ -0,0 +1,29 @@
#
# task: update
#
- name: Updates with APT
apt:
name: '*'
state: latest
update_cache: yes
only_upgrade: yes
#
# check for reboot
#
- name: Check if a reboot is needed for Debian and Ubuntu boxes
register: reboot_required_file
stat:
path: /var/run/reboot-required
- name: Reboot the Debian or Ubuntu server
reboot:
msg: "Reboot initiated by Ansible due to kernel updates"
connect_timeout: 5
reboot_timeout: 300
pre_reboot_delay: 0
post_reboot_delay: 30
test_command: uptime
when: reboot_required_file.stat.exists

@ -0,0 +1,17 @@
{{ ansible_managed | comment }}
# Overrides values from the fail2ban.conf configuration file.
# For comments relating to each setting see fail2ban.conf
[default]
loglevel = {{ common_fail2ban_loglevel }}
logtarget = {{ common_fail2ban_logtarget }}
syslogsocket = {{ common_fail2ban_syslog_socket }}
socket = /var/run/fail2ban/fail2ban.sock
pidfile = /var/run/fail2ban/fail2ban.pid
dbfile = /var/lib/fail2ban/fail2ban.sqlite3
dbpurgeage = {{ common_fail2ban_dbpurgeage }}
dbmaxmatches = {{ common_fail2ban_dbmaxmatches }}

@ -0,0 +1,56 @@
{{ ansible_managed | comment }}
[includes]
before = paths-debian.conf
[default]
# "ignoreip" can be an IP address, a CIDR mask or a DNS host. Fail2ban will not
# ban a host which matches an address in this list. Several addresses can be
# defined using space separator.
ignoreip = {{ common_fail2ban_ignoreips | join(' ') }}
ignoreself = {{ common_fail2ban_ignoreself }}
# The length of time between login attempts before a ban is set.
# For example, if Fail2ban is set to ban an IP after five (3) failed log-in attempts,
# those 3 attempts must occur within the set 10-minute findtime limit.
# The findtime value should be a set number of seconds.
findtime = {{ common_fail2ban_findtime }}
# max retries, before ban
maxretry = {{ common_fail2ban_maxretry }}
# reverse-Lookup-behavior
usedns = warn
# mail
destemail = root@localhost
sendername = Fail2Ban
mta = sendmail
#
chain = INPUT
#
# Actions
#
action_ = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
action_mw = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
%(mta)s-whois[name=%(__name__)s, dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s", sendername="%(sendername)s"]
action_mwl = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
%(mta)s-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s", sendername="%(sendername)s"]
action = %(action_)s
# "bantime" is the number of seconds that a host is banned.
bantime = {{ common_fail2ban_bantime }}
# "banaction"
banaction = ufw
# backend
backend = auto
# tcp, udp, icmp, all
protocol = {{ common_fail2ban_protocol }}
Loading…
Cancel
Save