fail2ban не добавляет правила nftables (Ubuntu 16), чиним!

Внезапно — в Ubuntu 16 корявый пакет fail2ban (10.2), которому не доложили конфигов! Поэтому ключевое:

 
 
  1. # идем куда нeдосыпали
  2. cd /etc/fail2ban/action.d/
  3. # бэкапим штатные (хоть и кривые) конфиги
  4. mv nftables-multiport.conf nftables-multiport.conf.bak
  5. mv nftables-allports.conf nftables-allports.conf.bak
  6. # качаем правильные конфиги
  7. wget https://raw.githubusercontent.com/fail2ban/fail2ban/master/config/action.d/nftables-allports.conf
  8. wget https://raw.githubusercontent.com/fail2ban/fail2ban/master/config/action.d/nftables-multiport.conf
  9. # вот этого вообще не было!:
  10. wget https://raw.githubusercontent.com/fail2ban/fail2ban/master/config/action.d/nftables.conf

 

Что я делал дальше:

  1. Убедился что у меня есть штатный конфиг «/etc/fail2ban/jail.conf», полученный копированием «jail.conf.dpkg-dist» с последующим вырезанием секции с тюрьмами (для них у меня «/jail.d/» есть и незачем их смешивать)
    /etc/fail2ban/jail.conf
     
    1. #
    2. # WARNING: heavily refactored in 0.9.0 release. Please review and
    3. # customize settings for your setup.
    4. #
    5. # Changes: in most of the cases you should not modify this
    6. # file, but provide customizations in jail.local file,
    7. # or separate .conf files under jail.d/ directory, e.g.:
    8. #
    9. # HOW TO ACTIVATE JAILS:
    10. #
    11. # YOU SHOULD NOT MODIFY THIS FILE.
    12. #
    13. # It will probably be overwritten or improved in a distribution update.
    14. #
    15. # Provide customizations in a jail.local file or a jail.d/customisation.local.
    16. # For example to change the default bantime for all jails and to enable the
    17. # ssh-iptables jail the following (uncommented) would appear in the .local file.
    18. # See man 5 jail.conf for details.
    19. #
    20. # [DEFAULT]
    21. # bantime = 1h
    22. #
    23. # [sshd]
    24. # enabled = true
    25. #
    26. # See jail.conf(5) man page for more information
    27. # Comments: use '#' for comment lines and ';' (following a space) for inline comments
    28. [INCLUDES]
    29. #before = paths-distro.conf
    30. before = paths-debian.conf
    31. # The DEFAULT allows a global definition of the options. They can be overridden
    32. # in each jail afterwards.
    33. [DEFAULT]
    34. #
    35. # MISCELLANEOUS OPTIONS
    36. #
    37. # "ignorself" specifies whether the local resp. own IP addresses should be ignored
    38. # (default is true). Fail2ban will not ban a host which matches such addresses.
    39. #ignorself = true
    40. # "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban
    41. # will not ban a host which matches an address in this list. Several addresses
    42. # can be defined using space (and/or comma) separator.
    43. #ignoreip = 127.0.0.1/8 ::1
    44. # External command that will take an tagged arguments to ignore, e.g. <ip>,
    45. # and return true if the IP is to be ignored. False otherwise.
    46. #
    47. # ignorecommand = /path/to/command <ip>
    48. ignorecommand =
    49. # "bantime" is the number of seconds that a host is banned.
    50. bantime = 10m
    51. # A host is banned if it has generated "maxretry" during the last "findtime"
    52. # seconds.
    53. findtime = 10m
    54. # "maxretry" is the number of failures before a host get banned.
    55. maxretry = 5
    56. # "backend" specifies the backend used to get files modification.
    57. # Available options are "pyinotify", "gamin", "polling", "systemd" and "auto".
    58. # This option can be overridden in each jail as well.
    59. #
    60. # pyinotify: requires pyinotify (a file alteration monitor) to be installed.
    61. # If pyinotify is not installed, Fail2ban will use auto.
    62. # gamin: requires Gamin (a file alteration monitor) to be installed.
    63. # If Gamin is not installed, Fail2ban will use auto.
    64. # polling: uses a polling algorithm which does not require external libraries.
    65. # systemd: uses systemd python library to access the systemd journal.
    66. # Specifying "logpath" is not valid for this backend.
    67. # See "journalmatch" in the jails associated filter config
    68. # auto: will try to use the following backends, in order:
    69. # pyinotify, gamin, polling.
    70. #
    71. # Note: if systemd backend is chosen as the default but you enable a jail
    72. # for which logs are present only in its own log files, specify some other
    73. # backend for that jail (e.g. polling) and provide empty value for
    74. # journalmatch. See https://github.com/fail2ban/fail2ban/issues/959#issuecomment-74901200
    75. backend = auto
    76. # "usedns" specifies if jails should trust hostnames in logs,
    77. # warn when DNS lookups are performed, or ignore all hostnames in logs
    78. #
    79. # yes: if a hostname is encountered, a DNS lookup will be performed.
    80. # warn: if a hostname is encountered, a DNS lookup will be performed,
    81. # but it will be logged as a warning.
    82. # no: if a hostname is encountered, will not be used for banning,
    83. # but it will be logged as info.
    84. # raw: use raw value (no hostname), allow use it for no-host filters/actions (example user)
    85. usedns = warn
    86. # "logencoding" specifies the encoding of the log files handled by the jail
    87. # This is used to decode the lines from the log file.
    88. # Typical examples: "ascii", "utf-8"
    89. #
    90. # auto: will use the system locale setting
    91. logencoding = auto
    92. # "enabled" enables the jails.
    93. # By default all jails are disabled, and it should stay this way.
    94. # Enable only relevant to your setup jails in your .local or jail.d/*.conf
    95. #
    96. # true: jail will be enabled and log files will get monitored for changes
    97. # false: jail is not enabled
    98. enabled = false
    99. # "mode" defines the mode of the filter (see corresponding filter implementation for more info).
    100. mode = normal
    101. # "filter" defines the filter to use by the jail.
    102. # By default jails have names matching their filter name
    103. #
    104. filter = %(__name__)s[mode=%(mode)s]
    105. #
    106. # ACTIONS
    107. #
    108. # Some options used for actions
    109. # Destination email address used solely for the interpolations in
    110. # jail.{conf,local,d/*} configuration files.
    111. destemail = root@localhost
    112. # Sender email address used solely for some actions
    113. sender = root@<fq-hostname>
    114. # E-mail action. Since 0.8.1 Fail2Ban uses sendmail MTA for the
    115. # mailing. Change mta configuration parameter to mail if you want to
    116. # revert to conventional 'mail'.
    117. mta = sendmail
    118. # Default protocol
    119. protocol = tcp
    120. # Specify chain where jumps would need to be added in ban-actions expecting parameter chain
    121. chain = <known/chain>
    122. # Ports to be banned
    123. # Usually should be overridden in a particular jail
    124. port = 0:65535
    125. # Format of user-agent https://tools.ietf.org/html/rfc7231#section-5.5.3
    126. fail2ban_agent = Fail2Ban/%(fail2ban_version)s
    127. #
    128. # Action shortcuts. To be used to define action parameter
    129. # Default banning action (e.g. iptables, iptables-new,
    130. # iptables-multiport, shorewall, etc) It is used to define
    131. # action_* variables. Can be overridden globally or per
    132. # section within jail.local file
    133. banaction = nftables-multiport
    134. banaction_allports = nftables-allports
    135. # The simplest action to take: ban only
    136. action_ = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
    137. # ban & send an e-mail with whois report to the destemail.
    138. action_mw = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
    139. %(mta)s-whois[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"]
    140. # ban & send an e-mail with whois report and relevant log lines
    141. # to the destemail.
    142. action_mwl = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
    143. %(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
    144. # See the IMPORTANT note in action.d/xarf-login-attack for when to use this action
    145. #
    146. # ban & send a xarf e-mail to abuse contact of IP address and include relevant log lines
    147. # to the destemail.
    148. action_xarf = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
    149. xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath=%(logpath)s, port="%(port)s"]
    150. # ban IP on CloudFlare & send an e-mail with whois report and relevant log lines
    151. # to the destemail.
    152. action_cf_mwl = cloudflare[cfuser="%(cfemail)s", cftoken="%(cfapikey)s"]
    153. %(mta)s-whois-lines[name=%(__name__)s, sender="%(sender)s", dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
    154. # Report block via blocklist.de fail2ban reporting service API
    155. #
    156. # See the IMPORTANT note in action.d/blocklist_de.conf for when to use this action.
    157. # Specify expected parameters in file action.d/blocklist_de.local or if the interpolation
    158. # `action_blocklist_de` used for the action, set value of `blocklist_de_apikey`
    159. # in your `jail.local` globally (section [DEFAULT]) or per specific jail section (resp. in
    160. # corresponding jail.d/my-jail.local file).
    161. #
    162. action_blocklist_de = blocklist_de[email="%(sender)s", service=%(filter)s, apikey="%(blocklist_de_apikey)s", agent="%(fail2ban_agent)s"]
    163. # Report ban via badips.com, and use as blacklist
    164. #
    165. # See BadIPsAction docstring in config/action.d/badips.py for
    166. # documentation for this action.
    167. #
    168. # NOTE: This action relies on banaction being present on start and therefore
    169. # should be last action defined for a jail.
    170. #
    171. action_badips = badips.py[category="%(__name__)s", banaction="%(banaction)s", agent="%(fail2ban_agent)s"]
    172. #
    173. # Report ban via badips.com (uses action.d/badips.conf for reporting only)
    174. #
    175. action_badips_report = badips[category="%(__name__)s", agent="%(fail2ban_agent)s"]
    176. # Report ban via abuseipdb.com.
    177. #
    178. # See action.d/abuseipdb.conf for usage example and details.
    179. #
    180. action_abuseipdb = abuseipdb
    181. # Choose default action. To change, just override value of 'action' with the
    182. # interpolation to the chosen action shortcut (e.g. action_mw, action_mwl, etc) in jail.local
    183. # globally (section [DEFAULT]) or per specific section
    184. action = %(action_)s
  2. Создал файл «/etc/fail2ban/jail.local», который читается после «/etc/fail2ban/jail.conf» и служит для переопределения параметров
    /etc/fail2ban/jail.local
     
    1. [DEFAULT]
    2. banaction = nftables-multiport
    3. banaction_allports = nftables-allports

Все! В инете есть еще варианты назначить конкретную таблицу для правил f2b, но вот с такой конфигурацией он и сам прекрасно добавляет таблицу имени себя:

 
 
  1. table inet f2b-table {
  2.         set addr-set-sshd {
  3.                 type ipv4_addr
  4.                 elements = { 31.220.1.210, 37.49.226.161 }
  5.         }
  6.         set addr-set-wp-qiwichupa-net {
  7.                 type ipv4_addr
  8.                 elements = { 34.76.172.157, 34.82.91.206,
  9.                              35.183.87.236, 35.220.162.15 }
  10.         }
  11.         chain f2b-chain {
  12.                 type filter hook input priority -1; policy accept;
  13.                 tcp dport { ssh } ip saddr @addr-set-sshd reject
  14.                 tcp dport { http, https } ip saddr @addr-set-wp-qiwichupa-net reject
  15.         }
  16. }

Единственное что имеет смысл, создать файлик /etc/fail2ban/action.d/nftables-common.local:

 
 
  1. [Init]
  2. blocktype = drop

Он также будет читаться как конфиг nftables и заставит дропать, а не реджектить пакеты с заблокированных айпишников.

И на эту дичь было убито 4 часа -_-

Защита wordpress с помощью fail2ban

Совершенно не моё изобретение, да и гуглится несложно, но тем не менее не грех будет упомянуть о средствах контрацепции для этого нашего вордпресса, чтобы на просторах интернетика было ему безопасно.

Вообще говоря, защита учеток вордпресса — это совсем отдельная тема, в которую можно погружаться и погружаться. Ботнеты ищут инсталляции ВП и стараются атаковать, как правило, с двух сторон: разумеется это wp-login.php — страница аутентификации с логином и паролем; и xmlrpc.php — кусок механизма для удаленного подключения и управления сайтом. Причем большинство атак приходится на вторую сторону, и в интернете куча инфы (копия) о том, как эту лавочку прикрыть, отрубив весь механизм. На самом деле это вполне дельная мысль, так как велика вероятность что удаленный доступ к сайту вовсе не используется, но если есть желание, например, постить через мобильный клиент вордпресса — он понадобится.

Но вне зависимости от отключения  xmlrpc, придется защищать и основную форму логона. Опять же тут можно (и нужно) возвести многоступенчатую защиту. Негрешно воткнуть капчу при помощи  WordPress ReCaptcha Integration, хорошей мыслью будет вообще запретить или ограничить регистрацию с логином и паролем, включив авторизацию через социальные сети (с этим поможет Social Login). Однако все неплохо, пока на сайт пытаются ломиться люди, но вышеупомянутые ботнеты любят атаковать с пачек IP-адресов, посылая частые запросы на аутентификацию, и тут я подбираюсь к сабжу…

Хотя на самом деле скажу еще одно: для блокировки пользователей, активно брутящих пароли, можно с помощью неплохого плагина для самого вордпресса — Limit Login Attempts Reloaded. Он вполне шикарен — ведет список IP, банит на установленный срок при превышении лимита попыток, умеет писать об этом в почту админа. Но в какой-то момент мне захотелось, чтобы такие айпишники не просто банились на уровне ВП, а выпиливались на файрволле — это более концептуальное решение, которое ко всему прочему разгружает сервер от левых запросов. Ну и конечно для такого типа задач есть готовое решение — демон fail2ban.

Оказывается прикрутить его к вордпрессу — проще простого.

Для начала нужно в папку «wp-content/mu-plugins» (если ее нет — нужно ее создать) положить файлик:

return_403_on_failed_login.php
 
  1. <?php
  2. /**
  3.  *  * Plugin Name: Return 403 on Failed Login
  4.  *   */
  5. function my_login_failed_403() {
  6.     status_header( 403 );
  7. }
  8. add_action( 'wp_login_failed', 'my_login_failed_403' );

Этот файлик, положенный в эту папку, будет постоянно работающим плагином, который нельзя отключить (папка mu-plugins — именно для таких). Суть его работы в том, чтобы при неудачной попытке залогиниться, в лог вебсервера сообщение о доступе к файлам wp-login.php и xmlrpc.php падало с 403й ошибкой. Это нужно чтобы отличать такие фейлы от простого запроса страниц. Выглядеть это будет примерно так:

 
 
  1. 143.255.155.196 - - [30/Apr/2019:09:11:01 +0300] "POST /wp-login.php HTTP/1.1" 403 2947 "https://qiwichupa.net/wp-login.php" "Mozilla/5.0 (Windows NT 10.0; rv:48.0) Gecko/20100101 Firefox/48.0"

Теперь нужно пошаманить с fail2ban.

Добавляем фильтр таких сообщений:

/etc/fail2ban/filter.d/wp-login.conf
 
  1. [Definition]
  2. failregex = <HOST>.*POST.*(wp-login\.php|xmlrpc\.php).* 403

Простая регулярка, которая выхватит IP из строки с 403ей ошибкой и нужными именами файлов.

Теперь настроим тюрьму:

/etc/fail2ban/jail.d/wordpress.conf
 
  1. [wp-mysite]
  2. enabled = true
  3. port = http,https
  4. filter = wp-login
  5. logpath = /var/log/nginx/mysite_access.log
  6. findtime = 600
  7. maxretry = 2
  8. bantime = 86400

Тут нужно обратить внимание на параметр logpath — он должен вести к аксес-логу вебсервера (apache или nginx, если он стоит перед апачем). Параметры findtime и bantime принимают значение в секундах, первый отвечает за интервал в который с одного айпишника должны упасть фейлы для срабатывания блокировки, второй — время бана. Так данный пример конфига отправит айпишник в бан на сутки, если с него за 10 минут прилетит больше двух неудачных попыток аутентификации.

Собственно и всё. Рестартим fail2ban, смотрим в /var/log/fail2ban.log — в нем должно быть написано что тюрьма запустилась, а спустя какое-то время, если на сайт полезли боты, и сообщения о банах должны появиться:

 
 
  1. 2019-04-30 00:42:10,120 fail2ban.jail : INFO Jail 'wp-mysite' started
  2. 2019-04-30 00:42:37,452 fail2ban.actions: WARNING [wp-mysite] Ban 119.160.136.138
  3. 2019-04-30 00:42:37,483 fail2ban.actions: WARNING [wp-mysite] Ban 143.255.155.57