とーますメモ

Ruby on Rails / Goなどの学習メモ

【fail2ban】WordPress用の独自フィルタ作成

以下のログに対応したものを作成する。

・wp-login.phpに対してPOSTをリクエストを送っているもの

※ 管理者のみがwp-login.phpするものなら良いが、不特定多数(woocommerceなど)が利用する場合は
wpのフィルター機能と連携して作成するのが良いと思う。
fail2banでWordPressを守る 2019年1月 - がとらぼ

フィルターの作成

$ vi /etc/fail2ban/filter.d/wordpress.local

failregexに、正規表現で検知したいログを記載します。 はIPアドレスに値します。

wordpress.local

[Definition]

# wp-login.phpの認証時に200番を返す場合は認証失敗を意味し、301番を返す場合は成功を意味する。
failregex = ^<HOST>.* "POST .*/wp-login.php([/\?#\\].*)? HTTP/.*" 200

ignoreregex =

フィルターの動作確認

以下のコマンドで対象ログの行が出ていれば成功

$ # fail2ban-regex access.logのパス /etc/fail2ban/filter.d/wplogin.local  --print-all-matched

監視設定

vi /etc/fail2ban/jail.conf に追記

[wordpress]
enabled = true
port = http,https
logpath = access.logのパス
maxretry = 5

再起動

$ systemctl restart fail2ban.service

【fail2ban】基本的な使い方

概要

Fail2banは、誤解を恐れずに言うと

1)様々な種類のログファイルを監視して、
2)特定のパターンにマッチしたログ情報(主にアクセス元IP)を元に
3)アクション(主にiptablesで接続拒否)を行う

ためのツール。

2)の特定のパターンにマッチさせる設定ファイルは「フィルター」と呼ばれ
デフォルトで入っているフィルターをそのまま使う、またはカスタマイズするか、
独自フィルターを作成することも可能

デフォルトで使用できるフィルターはsshdだけではなく
postfix, qmail, dovecot, proftpd, vsftpd, apache, PHP, phpmyadmin, nginx, mysql 等の有名所のログにも対応しているフィルターが入っている。

3)のアクション内容が定義された設定ファイルはiptablesだけではなく、sendmail, cloudflare, abuseipdb 等がある。
また独自アクションも作成可能。ちなみにアクションは複数設定が可能(例:攻撃元サイトをブロックし、sendmailで通知)

詳細は以下のサイトを参考にされたし。
不正アクセスからサーバを守るfail2ban。さくらのクラウド、VPSで使ってみよう! | さくらのナレッジ
第94回 サイトの防御とFail2ban[その1]:玩式草子─ソフトウェアとたわむれる日々|gihyo.jp … 技術評論社
fail2ban で対象 IP アドレスの第 2 オクテット(Class B)から遮断する - Qiita

※ [追記]
自分の勝手な勘違いだが、既に存在しているログファイルの行に対してもfail2banが対象になると思っていたが
それは勘違いで、fail2banがBanしてくれるログはあくまでも”監視中"のもののみのよう。

ログファイルの場所

/var/log/fail2ban.log

設定

主に以下の2つ。
1)/etc/fail2ban/fail2ban.conf・・・一般設定を行うファイル
2)/etc/fail2ban/jail.conf・・・どのログファイルを監視するかの設定を行うファイル。

ただし上記のファイルを直接触れるのは、fail2ban更新時に設定がすべて消えてしまうためご法度。
そのため、以下のファイルを別途新規に作る。

3)/etc/fail2ban/fail2ban.local
4)/etc/fail2ban/jail.local

3)と4)は1)と2)の内容を受け継ぎ、1)と2)の設定を上書きする。
そのため全部の設定を書く必要はなく、上書きしたい設定のみを書けば良い。

fail2ban.localの例

[Defination]
# change loglevel - CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG
loglevel = INFO

# change log file location if required
logtarget = /var/log/fail2ban.log

# change pidfile location if required
pidfile = /var/run/fail2ban/fail2ban.pid

/etc/fail2ban/jail.localの例でsshdのみを設定

[DEFAULT]

# "bantime" is the number of seconds that a host is banned.
bantime = 15m

# A host is banned if it has generated "maxretry" during the last "findtime" seconds.
findtime  = 15m

# "maxretry" is the number of failures before a host get banned.
maxretry = 6

[ssh]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 4

現在の設定の確認コマンド

$ fail2ban-client status

特定のJailの確認コマンド

ex) sshd

$ fail2ban-client status sshd

独自フィルタと独自アクション

独自フィルタは「/etc/fail2ban/filter.d/」内に作成する。
拡張子は「.conf」か「.local」かを選べるがデフォルトのものと見分けるために「.local」で作成する方が良い。

独自アクション「/etc/fail2ban/action.d/」内に作成する。
独自フィルタと同じく、拡張子は「.local」を使用する。

詳細は別の記事で。


[参考]
How to use Fail2ban on Ubuntu 18.04 - LaymanClass
Using Fail2ban to Secure Your Server - A Tutorial | Linode

【Nginx】OCSP Staplingについて

Nginxのエラーログを見てたら「OCSP_basic_verify() failed・・・」というエラーが出ていたので調べてみた。

OCSP Staplingとは何か?の説明については以下のサイトの説明がわかりやすい。
OCSP Stapling
nginx で OCSP Stapling (RapidSSL) | | 1Q77
How To Configure OCSP Stapling on Apache and Nginx | DigitalOcean

要はSSLが失効しているかをチェックするためのプロトコルということ。

以下のコマンドを実行すれば設定の有無がわかる。

$ echo QUIT | openssl s_client -connect localhost:443 -status 2> /dev/null | head -n 20

「OCSP Response Status: successful」:設定されている
「OCSP response: no response sent」:設定されていない

または以下のサイトにアクセスして、確認する手もある。
その際には「Do not show the results on the boards」にチェックを念の為に入れておいたほうが良いだろう。
SSL Server Test (Powered by Qualys SSL Labs)

【fail2ban】Ubuntu 18.04上で単純なsshdフィルターのみ使用しているのに、IPがbanされない現象

結論から言うとbackendに、pollingを指定していたのが原因だった。
ちなみにFail2banのバージョンは「v0.10.2」

backend: 対象のログについての指定。 auto, pyinotify, gamin, systemd, pollingの5つから選択可能。 systemdを選択すると、systemdのjournalから情報を取得します。 autoはpyinotify, gamin, systemd, pollingの順番に情報を取得しますが、たまにgaminで失敗するそうです。 任意のログファイルを指定するのであれば、pollingを指定しておけば問題ないと思います。

by WordPressの不正ログインと不正中継メール送信をfail2banでbanしてみた - Qiita

どの記事をみて、pollingの設定をしていたのかは忘れたが、この値をautoにしたところ、ちゃんとIPをBanするようになった。

[追記]

systemdが選択されるとlogpathは無視してjournalmatchを使ってどのログファイルを監視するかを決める

↑ systemdを選択するときは注意が必要そう。。。。

[参考]
fail2banのbackend問題 – NorthPage

Amazon LightsailでSSL設定をするときに注意すべき点

かなりハマった。
Amazon Lightsailには簡易ファイヤーウォールがあり、デフォルトだと22番と80番しか空いてないため
ufwなどで443を開けてしまっていても、この簡易ファイヤーウォールのせいで、443が遮断されてしまう。

注意してほしい。

【Chrome】httpのアドレスを入力してるのに、強制的にhttpsでリクエストされてしまう現象を止める方法

以下参考
How to stop an automatic redirect from “http://” to “https://” in Chrome - Super User

chrome://net-internals/#hstsを開いて、
「Delete domain security policies」の項目に、対象のURLを入力して、Deleteボタンを押せば強制的にhttpsリクエストにされるなくなる。
また「chrome://settings/clearBrowserData」を開いて、キャッシュを消しておく。

自分の場合は、キャッシュだけ消せば、httpsでリクエストされなくなった。

【Ansible】Ubuntu 18.04でのMySQL設定

MySQLの設定ファイルであるmy.cnfの場所の確認方法

$ mysql --help | grep my.cnf
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf

左から順に読み込まれる。

Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf

by mysql --help

要は以下の順

① /etc/my.cnf
② /etc/mysql/my.cnf
③ ~/.my.cnf

ただし、同じ設定値が後続のファイルにある場合、後続の設定値で上書きされるので注意!
my.cnf の読み込む順番でハマったのでまとめる - Qiita
第31回 MySQLのオプションファイル my.cnfの豆知識[その1]:MySQL道普請便り|gihyo.jp … 技術評論社
my.cnfに設定したsql_modeが効かなくて困った話

※1 設定ファイルを見つけるコマンド

$ sudo find / -name "*my*cnf"

※2 有効な設定オプション一覧を確認するコマンド

$ my_print_defaults mysqld


MySQLをインストール後、①と③は存在していないが②は存在している。
シンプルに②の内容を気にせず設定をするのであれば、①に新規で設定を書いてもよいが
自分の場合は、既にある設定をベースに、必要な設定を上書きする方式で設定を行いため
②のファイルを変更するやり方を選択する。

※ ちなみに~/.my.cnfについては以下のような情報を見つけた。

To keep the things simple place all custom server settings into the /etc/my.cnf file. Some user-specific options can be stored in the ~/.my.cnf where ~/ means "the homedir for the given system user". Also note the leading dot in the user-specific file name. That file is used for command-line client utilities like mysql or mysqldump only, not for server. Therefore the [mysqld] section can be completely omitted in the user-specific config file.

mysql - Where should I store a custom ".my.cnf" file? - Database Administrators Stack Exchange
要は、~/.my.cnfの設定は、mysqlやmysqldumpなどのコマンド使用時のみ有効であり、mysql全体の設定としては有効ではないとのことらしい。
そして設定の中で比重が多い[mysqld]の設定は、完全に除外されるとのこと。まあなぜこの設定が各ユーザごとに設定が可能なホームディレクトリに設定できるのかを
考えれば納得が行く。じゃあどういう場合に使用するのかというと、以下のリンクのようなIDやパスワードを省略してログインしたい場合などがある。
.my.cnf - mysql user & password


②のファイルの中身を見ると以下の記述がある。

#
# The MySQL database server configuration file.
#

.....

# * IMPORTANT: Additional settings that can override those from this file!
#   The files must end with '.cnf', otherwise they'll be ignored.
#

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/

「!includedir」というディレクティブがあるが、これは指定されたディレクトリ内の.cnfファイルを設定ファイルとして読み込むという意味。
各ディレクトリを見たが、通常設定ファイルが書かれているファイルは「/etc/mysql/mysql.conf.d/mysqld.cnf」だった。
ただし設定を変更または追記するのに、このファイルを弄るのではなく、設定が後続の設定で上書きされる仕組みを活かし
「/etc/mysql/my.cnf」に追記する方式で設定を記述する。

tasks/main.yml

---
# @see https://github.com/bennojoy/mysql/blob/master/tasks/main.yml

# @see https://stackoverflow.com/questions/42267299/ansible-install-mysql-5-7-set-root-user-password?rq=1
- name: Specify MySQL root password before installing
  debconf: name='mysql-server' question='mysql-server/root_password' value='{{mysql_root_user_pass | quote}}' vtype='password'
  tags: mysql

- name: Confirm MySQL root password before installing
  debconf: name='mysql-server' question='mysql-server/root_password_again' value='{{mysql_root_user_pass | quote}}' vtype='password'
  tags: mysql

- name: Install the mysql packages in Debian derivatives
  apt:
    name: ['mysql-server']
    state: present
  tags: mysql

# select user,host,authentication_string from mysql.user;
#
# @see http://docs.ansible.com/ansible/latest/modules/mysql_user_module.html
# check_implicit_admin: Check if mysql allows login as root/nopassword before trying supplied credentials.
- name: remove anonymous users by empty name
  mysql_user: name='' host_all=yes login_user=root login_password={{ mysql_root_user_pass }} check_implicit_admin=yes state=absent

- name: Create the database's
  mysql_db: name={{ item.name }} login_user=root login_password={{ mysql_root_user_pass }} encoding=utf8 state=present
  with_items:
    - "{{ mysql_db }}"
  when: mysql_db|lower() != 'none'
  tags: mysql

- name: Create the database users
  mysql_user:
    name:                 "{{ item.name }}"
    password:             "{{ item.pass }}"
    priv:                 '{{ item.priv|default("*.*:ALL") }}'
    state:                present
    host:                 '{{ item.host | default("localhost") }}'
    login_password:       "{{ mysql_root_user_pass }}"
    check_implicit_admin: yes
  with_items:
    - "{{ mysql_users }}"
  when: mysql_users|lower() != 'none'
  tags: [mysql, mysql_user]

- name: Append my.cnf into {{ mysql_config_file }}
  blockinfile:
    dest: "{{ mysql_config_file }}"
    block: "{{ lookup('template', 'cnf.j2') }}"
    marker: "# {mark} ANSIBLE MANAGED BLOCK"
  notify: restart mysql
  tags: [mysql, mysql_config]

- name: Create slow query log file (if configured).
  command: "touch {{ mysql_slow_query_log_file }}"
  args:
    creates: "{{ mysql_slow_query_log_file }}"
    warn: false
  when: mysql_slow_query_log_enabled
  tags: [mysql, mysql_config]

- name: Set ownership on slow query log file (if configured).
  file:
    path: "{{ mysql_slow_query_log_file }}"
    state: file
    owner: mysql
    group: mysql
    mode: 0640
  when: mysql_slow_query_log_enabled
  tags: [mysql, mysql_config]

- name: Start the mysql services
  service: name=mysql state=started enabled=yes
  tags: mysql

# @see: https://qiita.com/park-jh/items/34e6434d71e685a48f07#mysqld---initialize-insecure%E3%81%AE%E5%A0%B4%E5%90%88
- name: run mysql_secure_installation
  command: mysql_secure_installation -u root -p'{{ mysql_root_user_pass }}' -D
  changed_when: false
  tags: mysql

defaults/main.yml

---

# @see: https://qiita.com/mamy1326/items/9c5eaee3c986cff65a55
# @see: https://github.com/geerlingguy/ansible-role-mysql

mysql_config_file: /etc/mysql/my.cnf

mysql_sql_mode: ''

# Slow query log settings.
mysql_slow_query_log_enabled: true
mysql_slow_query_log_file: /var/log/mysql-slow.log
mysql_slow_query_time: "2"



# Memory settings (default values optimized ~512MB RAM).
mysql_key_buffer_size: "256M"
mysql_max_allowed_packet: "64M"
mysql_table_open_cache: "256"
mysql_sort_buffer_size: "1M"
mysql_read_buffer_size: "1M"
mysql_read_rnd_buffer_size: "4M"
mysql_myisam_sort_buffer_size: "64M"
mysql_thread_cache_size: "8"
mysql_query_cache_type: "0"
mysql_query_cache_size: "16M"
mysql_query_cache_limit: "1M"
mysql_max_connections: "151"              # default: 151
mysql_tmp_table_size: "16M"
mysql_max_heap_table_size: "16M"
mysql_group_concat_max_len: "1024"
mysql_join_buffer_size: "262144"



# InnoDB settings.
mysql_innodb_file_per_table: "1"
# Set .._buffer_pool_size up to 80% of RAM but beware of setting too high.    ex) server memory: 8G => 6G
mysql_innodb_buffer_pool_size: "1600M"
# Set .._log_file_size to 25% of buffer pool size. but maximum size is 64M    ex) innodb_buffer_pool_size: 6G => 1.25G
mysql_innodb_log_file_size: "64M"
mysql_innodb_log_buffer_size: "16M"
mysql_innodb_flush_log_at_trx_commit: "1"
mysql_innodb_lock_wait_timeout: "50"

templates/cnf.j2

[mysqld]
{% if mysql_sql_mode %} sql_mode = {{ mysql_sql_mode }} {% endif %}

{% if mysql_slow_query_log_enabled %}
# Slow query log configuration.
slow_query_log = 1
slow_query_log_file = {{ mysql_slow_query_log_file }}
long_query_time = {{ mysql_slow_query_time }}
{% endif %}



# Memory settings.
# key_buffer_size = {{ mysql_key_buffer_size }}
# max_allowed_packet = {{ mysql_max_allowed_packet }}
# table_open_cache = {{ mysql_table_open_cache }}
# sort_buffer_size = {{ mysql_sort_buffer_size }}
# read_buffer_size = {{ mysql_read_buffer_size }}
# read_rnd_buffer_size = {{ mysql_read_rnd_buffer_size }}
# myisam_sort_buffer_size = {{ mysql_myisam_sort_buffer_size }}
# thread_cache_size = {{ mysql_thread_cache_size }}
# query_cache_type = {{ mysql_query_cache_type }}
# query_cache_size = {{ mysql_query_cache_size }}
# query_cache_limit = {{ mysql_query_cache_limit }}
# max_connections = {{ mysql_max_connections }}
# tmp_table_size = {{ mysql_tmp_table_size }}
# max_heap_table_size = {{ mysql_max_heap_table_size }}
# group_concat_max_len = {{ mysql_group_concat_max_len }}
# join_buffer_size = {{ mysql_join_buffer_size }}



# InnoDB settings.
innodb_file_per_table = {{ mysql_innodb_file_per_table }}
innodb_buffer_pool_size = {{ mysql_innodb_buffer_pool_size }}
innodb_log_file_size = {{ mysql_innodb_log_file_size }}
innodb_log_buffer_size = {{ mysql_innodb_log_buffer_size }}
innodb_flush_log_at_trx_commit = {{ mysql_innodb_flush_log_at_trx_commit }}
innodb_lock_wait_timeout = {{ mysql_innodb_lock_wait_timeout }}


[参考]
How do I identify the MySQL my.cnf file?