Apache mod_qos WordPress bruteforce mitigation

Hi, WordPress bruteforce attacks produce high cpu load
here some simple examples to get rid of that issue with mod_qos

Install apache module and enable it

apt install libapache2-mod-qos
a2enmod unique_id qos setenvif

For global mitigation, edit your apache module config

<IfModule qos_module>
  # minimum request rate (bytes/sec at request reading):
  #QS_SrvRequestRate                                 120

  # limits the connections for this virtual host:
  #QS_SrvMaxConn                                     100

  # allows keep-alive support till the server reaches 600 connections:
  #QS_SrvMaxConnClose                                600

  # allows max 50 connections from a single ip address:
  #QS_SrvMaxConnPerIP                                 50

  # allows a single IP addess to access the URI /wp-login.php not more
  # than 10 times within 2 minutes:
  SetEnvIf Request_URI ^/xmlrpc.php LimitWpXmlRpc
  QS_ClientEventLimitCount 10 120 LimitWpXmlRpc
  SetEnvIf Request_URI ^/wp-login.php LimitWpLogin
  QS_ClientEventLimitCount 10 120 LimitWpLogin

Per Virtualhost mitigation apache config

<IfModule qos_module>
  # limits concurrent requests to the locations:
  QS_LocRequestLimitMatch "^(/wp-login.php).*$" 2
  # does not allow more than 1 requests/sec:
  QS_LocRequestPerSecLimitMatch "^(/wp-login.php).*$" 1

  # limits concurrent requests to the locations:
  QS_LocRequestLimitMatch "^(/xmlrpc.php).*$" 2
  # does not allow more than 1 requests/sec:
  QS_LocRequestPerSecLimitMatch "^(/xmlrpc.php).*$" 1

Have fun!

freeradius 3.0 ubuntu 18.04 with daloradius mikrotik ikev2 eap-radius wireless


First of all setup your favorite php sql webserver

apt install php-db php-gd git freeradius freeradius-mysql

cd /var/www/web001/htdocs 
git clone https://github.com/lirantal/daloradius.git

We have to import the freeradius 3.0 mysql schema first. Daloradius does only have freeradius 2.0 compatible sql schemas.

cat /etc/freeradius/3.0/mods-config/sql/main/mysql/schema.sql | mysql -u radius -p radius

Now we import the daloradius sql schema without freeradius 2.0 sql schemas

cat /var/www/web001/htdocs/daloradius/contrib/db/mysql-daloradius.sql | mysql -u radius -p radius

here my freeradius mysql setup

cd /etc/freeradius/3.0/mods-enabled
ln -s ../mods-available/sql

vim sql

driver = "rlm_sql_mysql"
dialect = "mysql"
server = "localhost"
port = 3306
login = "radius"
password = "abcdefg"
radius_db = "radius"
read_clients = yes

here my changes to eap (eap for authenticating mikrotik wireless via wpa2 enterprise and mikrotik ikev2 eap radius)

vim /etc/freeradius/3.0/mods-enabled/eap

eap {
#ikev2 eap radius
default_eap_type = peap
tls-config tls-common {
private_key_file = path_to_your_ssl_private_key
certificate_file = path_to_your_ssl_certificate
ca_file = path_to_your_ssl_cabundle

I use rapidssl server certificate.


here my changes to the “default” site

cd /etc/freeradius/3.0/sites-enabled
vim default

authorize {

accounting {

session {

post-auth {

session {

here my bulk radius settings

cd /etc/freeradius/3.0

vim radiusd.conf

log {
auth = yes
auth_badpass = yes


you have to create a systemd override for the freeradius unit. otherwise freeradius won’t start correctly if mysql is not running.

systemctl edit freeradius

After=network.target mysql.service

setup daloradius config

vim /var/www/web001/htdocs/daloradius/library/daloradius.conf.php


Have fun!

awstats static html apache ubuntu howto


here some nice howto to get awstats with static html output working under ubuntu

apt-get install awstats libgeo-ipfree-perl libnet-ip-perl

disable the ubuntu cronjob ( comment out everything)

vim /etc/cron.d/awstats

now we setup our /etc/awstats/awstats.domain.tld.conf (cp awstats.conf awstats.domain.tld.conf)


I’ve disabled DNSLookup due to performance issues

create a shell script witch executes awstats


DOMS="domain.tld domain1.tld domain2.tld domain3.tld"

for DOM in $DOMS
/usr/lib/cgi-bin/awstats.pl -config=$DOM -update -LogFile="/usr/share/awstats/tools/logresolvemerge.pl /var/log/apache2/"$DOM"-access.log.1 /var/log/apache2/"$DOM"-access.log |"
/usr/share/awstats/tools/awstats_buildstaticpages.pl -update -config=$DOM -dir=/srv/www/vhosts/host1/$DOM/htdocs/awstats -awstatsprog=/usr/lib/cgi-bin/awstats.pl

Have fun!

radicale fcgid cardav caldav sync with davdroid and thunderbird


First get your radicale version an unpack it to your webspace:


I’m using at this time 0.9

Just unpack your radicale tarball in your DocumentRoot:
My .htaccess looks like this (for url rewriting and basic-auth):

AuthType Basic
AuthName "Radicale Authentication"
AuthBasicProvider file
AuthUserFile /var/www/bla/web/.htpasswd
Require valid-user

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ radicale.fcgi/$1 [QSA,L]

Now we have to create some directories in the homefolder of your webspace if your hoster is running suexec and mod_fcgi

mkdir -p /var/www/bla/.config/radicale

I moved the config examples from DocumentRoot to the newly created folder like this:

mv /var/www/bla/web/config /var/www/bla/.config/radicale/
mv /var/www/bla/web/logging /var/www/bla/.config/radicale/

My files are looking like this:

cat /var/www/bla/.config/radicale/config
# -*- mode: conf -*-
# vim:ft=cfg

# Config file for Radicale - A simple calendar server
# Place it into /etc/radicale/config (global)
# or ~/.config/radicale/config (user)
# The current values are the default ones

# CalDAV server hostnames separated by a comma
# IPv4 syntax: address:port
# IPv6 syntax: [address]:port
# For example:, [::]:9999
# IPv6 adresses are configured to only allow IPv6 connections
hosts =
# Daemon flag
daemon = False
# File storing the PID in daemon mode
pid =
# SSL flag, enable HTTPS protocol
ssl = False
# SSL certificate path
certificate = /etc/apache2/ssl/server.crt
# SSL private key
key = /etc/apache2/ssl/server.key
# SSL Protocol used. See python's ssl module for available values
protocol = PROTOCOL_SSLv23
# Ciphers available. See python's ssl module for available ciphers
ciphers =
# Reverse DNS to resolve client address in logs
dns_lookup = True
# Root URL of Radicale (starting and ending with a slash)
base_prefix = /
# Possibility to allow URLs cleaned by a HTTP server, without the base_prefix
can_skip_base_prefix = False
# Message displayed in the client when a password is needed
realm = Radicale - Password Required

# Encoding for responding requests
request = utf-8
# Encoding for storing local collections
stock = utf-8

# Authentication method
# Value: None | htpasswd | IMAP | LDAP | PAM | courier | http | remote_user | custom
type = None

# Custom authentication handler
custom_handler =

# Htpasswd filename
htpasswd_filename = /etc/radicale/users
# Htpasswd encryption method
# Value: plain | sha1 | crypt
htpasswd_encryption = crypt

# LDAP server URL, with protocol and port
ldap_url = ldap://localhost:389/
# LDAP base path
ldap_base = ou=users,dc=example,dc=com
# LDAP login attribute
ldap_attribute = uid
# LDAP filter string
# placed as X in a query of the form (&(...)X)
# example: (objectCategory=Person)(objectClass=User)(memberOf=cn=calenderusers,ou=users,dc=example,dc=org)
# leave empty if no additional filter is needed
ldap_filter =
# LDAP dn for initial login, used if LDAP server does not allow anonymous searches
# Leave empty if searches are anonymous
ldap_binddn =
# LDAP password for initial login, used with ldap_binddn
ldap_password =
# LDAP scope of the search
ldap_scope = OneLevel

# IMAP Configuration
imap_hostname = localhost
imap_port = 143
imap_ssl = False

# PAM group user should be member of
pam_group_membership =

# Path to the Courier Authdaemon socket
courier_socket =

# HTTP authentication request URL endpoint
http_url =
# POST parameter to use for username
http_user_parameter =
# POST parameter to use for password
http_password_parameter =

# Git default options
committer = Radicale <radicale@example.com>

# Rights backend
# Value: None | authenticated | owner_only | owner_write | from_file | custom
type = owner_write

# Custom rights handler
custom_handler =

# File for rights management from_file
file = ~/.config/radicale/rights

# Storage backend
# Value: filesystem | multifilesystem | database | custom
type = filesystem

# Custom storage handler
custom_handler =

# Folder for storing local collections, created if not present
filesystem_folder = ~/.config/radicale/collections

# Database URL for SQLAlchemy
# dialect+driver://user:password@host/dbname[?key=value..]
# For example: sqlite:///var/db/radicale.db, postgresql://user:password@localhost/radicale
# See http://docs.sqlalchemy.org/en/rel_0_8/core/engines.html#sqlalchemy.create_engine
database_url =

# Logging configuration file
# If no config is given, simple information is printed on the standard output
# For more information about the syntax of the configuration file, see:
# http://docs.python.org/library/logging.config.html
#config = ~/.config/radicale/logging
config = /var/log/radicale
# Set the default logging level to debug
debug = False
# Store all environment variables (including those set in the shell)
full_environment = False

# Additional HTTP headers
#Access-Control-Allow-Origin = *
cat /var/www/bla/.config/radicale/logging
# -*- mode: conf -*-
# vim:ft=cfg

# Logging config file for Radicale - A simple calendar server
# The default path for this file is /etc/radicale/logging
# This can be changed in the configuration file
# Other handlers are available. For more information, see:
# http://docs.python.org/library/logging.config.html

# Loggers, handlers and formatters keys

# Loggers names, main configuration slots
keys = root

# Logging handlers, defining logging output methods
#keys = console,file
keys = file

# Logging formatters
keys = simple,full

# Loggers

# Root logger
level = DEBUG
#handlers = console,file
handlers = file

# Handlers

# Console handler
class = StreamHandler
level = INFO
args = (sys.stdout,)
formatter = simple

# File handler
class = FileHandler
args = ('/var/www/bla/log/radicale.log',)
formatter = full

# Formatters

# Simple output format
format = %(message)s

# Full output format
format = %(asctime)s - %(levelname)s: %(message)s


Before we can fire up davdroid, your calender and your addressbook needs to be created. I’ve done this with thunderbird.
Davdroid only detect’s your calendar and your addressbook only if  it is in special url schema (end of line slashes mandatory). Here my url schema:


After successfully creation of our addressbook we can locate them via:

# ls /var/www/bla/.config/radicale/collections/htpasswdusername/addressbook.vcf/
Default.vcf Default.vcf.props
# ls /var/www/bla/.config/radicale/collections/htpasswdusername/calendar.ics/
Default.ics Default.ics.props

Now fire up your davdroid app and use as url (end of line slash mandatory)


Hope this will help someone, I had to invest some time to get this done.

apache2 reverseproxy for single virtualhost


need to reverseproxy a webite with apache?
following config for following scenario:

your webserver A is running on ip with bla.com
but you want that webserver B running bla.com proxying to webserver A

then put the following config in your configuration file of webserver B:

<VirtualHost *:80>
         <Proxy *>
                Order deny,allow
                Allow from all
         ServerName bla.com
         ServerAlias www.bla.com
         ServerAdmin webmaster@bla.com
         ProxyPreserveHost On
         ProxyPass /
         ProxyPassReverse /

If you are wondering what the option “ProxyPreserveHost” is read here:
http://httpd.apache. … ml#proxypreservehost
don’t forget to change dns settings.

Have fun!