不正アクセスに対する対応は当サイトでは悪質な(連続で何度もログインアタック攻撃をするなど)場合、手動でblocklist(ufwの場合、/etc/ufw/before.rules)に放り込むことにしている。そのため集計対象としてはブロックリストに含まれるIPアドレスは対象外としたい。よってapacheユーザーでブロックリストを読み込む必要があるが、こちらのファイルはroot権限でしか開くことが出来ない。apacheユーザーを管理者グループに入れることも考えたが、ここはひとつ。
- root権限でブロックリストを読み込むコマンドを作成する(getblocklist)。
- apacheユーザーに getblocklistコマンドだけパスワードなしでsudo出来るようにする。
- apacheユーザー権限で動作するpythonプログラム(djangoなど)でsubproccessにより上記コマンドを実行し、結果をリストとして得る関数を作る
で、バッチリではないかと、この方針で進めることとする。
1.root権限でブロックリストを取得するコマンド
これは全く問題なしで、以前のblockset.pyより該当部分のみを抜き出したプログラムとなる。
ipアドレスを利用するクラスipv4addressはライブラリとして分離している。
2.apacheユーザーに実行権限付加
表示プログラムを/usr/local/sbinなどに置きapacheユーザー(www-data)にsudo実行権限を与える
www-data ALL=(ALL) NOPASSWD:/usr/local/sbin/getblocklist.py
www-dataパスワード未設定のsu不可ユーザーなのでsudo ALLでも他のコマンドは実行出来ない。
3. apacheユーザー権限でリストを取得する
subproccessモジュールを利用して標準出力を得るには、Popenでプロセスを起動してプロセスの標準出力を接続する必要がある。プロセスのstdoutは一行づつ取り出すイテレータとなるので、mapによりipv4adrsetのリストとして取得している。(また、なぜかプロセスの出力がバイトとなっているので、UTF-8で文字列にデコードする必要があった”x.decode('UTF-8').rstrip('\n')
”)
import subprocess
get_blocklist_proccess_cmd = ['sudo','/usr/local/sbin/getblocklist.py']
# root権限でブロックリスト取得コマンドを走らせて標準入力を得る
def get_blocklist():
proc = subprocess.Popen(get_blocklist_proccess_cmd, stdout=subprocess.PIPE)
bipset = list(map(lambda x: ipv4adrset(x.decode('UTF-8').rstrip('\n')), proc.stdout))
return bipset
良く分かっていなかったのだけど、関数からmapのまま返すとイテレータなので関数の外にでてもプロセスが終わってないのね。。listとして全部取得してプロセスを正常に終らせておく必要がありましたとさ。