シェルスクリプトで業務を自動化

前回までで、シェルとシェルスクリプトについては一通り説明を行った。今度は機能を使って実用的なシェルスクリプトを作ってみよう。シェルやシェルスクリプトを使う最大の目的の1つは処理の自動化だ。人間が手動で処理するのではなく、コンピュータにデータの処理を行わせる。シェルスクリプトはその最も簡単な方法の1つだ。

今回はその例として、sshdのログファイルを解析して、大量に不正ログインを実行しようとしてくるホストのIPアドレスを取り出す方法を取り上げようと思う。サーバ管理者であれば、パブリックIPに接続した段階から、sshポートに対して不正ログインが試みられることはよくご存知だろう。

危険性の高いIPアドレスを取り出してファイアウォールにブロックを追加するというのは1つの運用手段だ。スクリプトを使うと完全に処理を自動化して、自動的にブロックルールを更新するといったこともできる。今回はログデータを整理する処理をシェルスクリプトで実施する例を紹介する。

ワンライナーでざっくり作成

この手の処理を行うときは、ワンライナーからはじめていくと処理が簡単だ。まず、次のようにしてログファイルからsshdに関するログデータを取り出してみる。これは「sshd[プロセスID]」という文字列が含まれている行を取り出す処理になっている。

# grep -E 'sshd\[[0-9]*\]' /var/log/auth.log | head -20
Jul 31 07:18:59 virt sshd[4600]: error: maximum authentication attempts exceeded for root from 170.80.226.29 port 51154 ssh2 [preauth]
Jul 31 07:19:07 virt sshd[4603]: error: maximum authentication attempts exceeded for root from 170.80.226.29 port 51158 ssh2 [preauth]
Jul 31 07:19:25 virt sshd[4610]: error: maximum authentication attempts exceeded for invalid user admin from 170.80.226.29 port 51170 ssh2 [preauth]
Jul 31 07:19:33 virt sshd[4614]: error: maximum authentication attempts exceeded for invalid user admin from 170.80.226.29 port 51176 ssh2 [preauth]
Jul 31 07:19:46 virt sshd[4621]: error: maximum authentication attempts exceeded for invalid user oracle from 170.80.226.29 port 51186 ssh2 [preauth]
Jul 31 23:40:29 virt sshd[47403]: error: maximum authentication attempts exceeded for root from 27.41.190.25 port 28590 ssh2 [preauth]
Aug  1 17:49:51 virt sshd[40750]: error: Received disconnect from 180.149.125.152 port 33524:7: Service not available [preauth]
Aug  2 08:22:45 virt sshd[37061]: error: maximum authentication attempts exceeded for root from 59.125.69.40 port 43106 ssh2 [preauth]
Aug  2 08:22:48 virt sshd[37063]: error: maximum authentication attempts exceeded for invalid user admin from 59.125.69.40 port 43111 ssh2 [preauth]
Aug  2 08:22:53 virt sshd[37067]: error: maximum authentication attempts exceeded for root from 59.125.69.40 port 43112 ssh2 [preauth]
Aug  2 08:22:55 virt sshd[37070]: error: maximum authentication attempts exceeded for root from 59.125.69.40 port 43116 ssh2 [preauth]
Aug  2 19:55:03 virt sshd[49535]: error: maximum authentication attempts exceeded for root from 223.199.221.178 port 38235 ssh2 [preauth]
Aug  2 22:44:10 virt sshd[53073]: error: maximum authentication attempts exceeded for root from 113.109.110.51 port 43758 ssh2 [preauth]
Aug  3 01:38:55 virt sshd[66889]: error: maximum authentication attempts exceeded for invalid user admin from 168.205.194.9 port 40676 ssh2 [preauth]
Aug  3 12:28:40 virt sshd[83372]: error: maximum authentication attempts exceeded for root from 14.118.206.120 port 33252 ssh2 [preauth]
Aug  3 12:39:09 virt sshd[83589]: error: maximum authentication attempts exceeded for root from 183.128.224.219 port 43194 ssh2 [preauth]
Aug  3 19:12:55 virt sshd[91666]: error: maximum authentication attempts exceeded for root from 113.17.31.61 port 43055 ssh2 [preauth]
Aug  4 01:42:05 virt sshd[7432]: error: maximum authentication attempts exceeded for root from 115.213.143.250 port 48576 ssh2 [preauth]
Aug  4 04:08:55 virt sshd[10828]: error: maximum authentication attempts exceeded for invalid user 1234 from 188.92.77.12 port 39217 ssh2 [preauth]
Aug  4 04:23:18 virt sshd[11119]: error: maximum authentication attempts exceeded for invalid user adm from 188.92.77.12 port 48984 ssh2 [preauth]
#

データとして欲しいのは日付データとログインを試みたユーザー名、それにアクセス元のIPアドレスといったところだ。次のようにawkコマンドをつなげて、該当する列だけを取り出していく。

# grep -E 'sshd\[[0-9]*\]' /var/log/auth.log | awk '{print $1,$2,$3,$12,$14}' | head -20
Jul 31 07:18:59 root 170.80.226.29
Jul 31 07:19:07 root 170.80.226.29
Jul 31 07:19:25 invalid admin
Jul 31 07:19:33 invalid admin
Jul 31 07:19:46 invalid oracle
Jul 31 23:40:29 root 27.41.190.25
Aug 1 17:49:51 33524:7: not
Aug 2 08:22:45 root 59.125.69.40
Aug 2 08:22:48 invalid admin
Aug 2 08:22:53 root 59.125.69.40
Aug 2 08:22:55 root 59.125.69.40
Aug 2 19:55:03 root 223.199.221.178
Aug 2 22:44:10 root 113.109.110.51
Aug 3 01:38:55 invalid admin
Aug 3 12:28:40 root 14.118.206.120
Aug 3 12:39:09 root 183.128.224.219
Aug 3 19:12:55 root 113.17.31.61
Aug 4 01:42:05 root 115.213.143.250
Aug 4 04:08:55 invalid 1234
Aug 4 04:23:18 invalid adm
#

必要のない行も含まれていることがわかる。IPアドレスが掲載されていない行は必要がないので、さらに次のようにIPアドレスが含まれている行だけをgrepコマンドで抽出していく。

# grep -E 'sshd\[[0-9]*\]' /var/log/auth.log | awk '{print $1,$2,$3,$12,$14}'| grep -E '[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}' | head -20
Jul 31 07:18:59 root 170.80.226.29
Jul 31 07:19:07 root 170.80.226.29
Jul 31 23:40:29 root 27.41.190.25
Aug 2 08:22:45 root 59.125.69.40
Aug 2 08:22:53 root 59.125.69.40
Aug 2 08:22:55 root 59.125.69.40
Aug 2 19:55:03 root 223.199.221.178
Aug 2 22:44:10 root 113.109.110.51
Aug 3 12:28:40 root 14.118.206.120
Aug 3 12:39:09 root 183.128.224.219
Aug 3 19:12:55 root 113.17.31.61
Aug 4 01:42:05 root 115.213.143.250
Aug 4 09:15:49 root 170.80.227.99
Aug 4 11:35:13 root 217.92.127.208
Aug 4 20:20:24 root 71.202.241.115
Aug 4 22:27:30 root 58.35.16.9
Aug 4 23:56:53 root 27.222.223.201
Aug 5 13:51:12 root 31.181.215.22
Aug 5 20:01:50 root 113.215.220.89
Aug 6 01:25:36 root 101.235.114.131
#

「[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}」はIPアドレスに一致する正規表現だ(厳密にIPアドレスのみに一致するわけではないのだが、この用途ではこれで十分だ)。データ抽出に関してはこのくらいで十分だろう。

シェルスクリプトに整理する

ワンライナーで基本となるコマンドの動作を確認したら、今度はワンライナーをシェルスクリプトにまとめていく。ここでは次のようにシェルスクリプト「ssh-check」をまとめてみた。

#!/bin/sh

logfile=/var/log/auth.log

# ログデータを取得
cat $logfile                        |
# sshdに関するログのみを選択
grep    -E 'sshd\[[0-9]*\]'                 |
# 必要なデータのみを抽出
#   1列目:    月
#   2列目:    日
#   3列目:    時:分:秒
#   12列目:   ユーザ名
#   14列目:   アクセス元IPアドレス
awk '{print $1,$2,$3,$12,$14}'              |
# 必要なログのみを選択
grep -E '[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}'     |
# 日付データを正規化(年月日時分秒)
while read mon day time user ip
do
    date=$(date --date="$mon $day $time" +%Y%m%d%H%M%S)
    echo "$date $user   $ip"
done
# 1:年月日時分秒 2:ユーザ名 3:アクセス元IPアドレス

ワンライナーとの違いは、最後に「Aug 6 01:25:36」といった感じで出力されている日付を20190806012536といった形式に整えている点にある。GNU coreutilsに含まれているdateコマンドは—dateオプションという機能があり、このような感じのフォーマット変換ができるようになっている。

また、最後にはタブ区切りでデータを出力させている。この辺りは、自分が使いやすいデータ形式にしてもらえればよい。カンマ区切りにすればCSVになる。

作成したssh-checkスクリプトを実行すると次のようになる。

# ./ssh-check
20190731071859  root    170.80.226.29
20190731071907  root    170.80.226.29
20190731234029  root    27.41.190.25
20190802082245  root    59.125.69.40
20190802082253  root    59.125.69.40
20190802082255  root    59.125.69.40
20190802195503  root    223.199.221.178
20190802224410  root    113.109.110.51
20190803122840  root    14.118.206.120
20190803123909  root    183.128.224.219
20190803191255  root    113.17.31.61
20190804014205  root    115.213.143.250
20190804091549  root    170.80.227.99
20190804113513  root    217.92.127.208
20190804202024  root    71.202.241.115
20190804222730  root    58.35.16.9
20190804235653  root    27.222.223.201
20190805135112  root    31.181.215.22
20190805200150  root    113.215.220.89
20190806012536  root    101.235.114.131
20190806041631  root    183.159.195.55
20190806083625  root    121.25.24.86
20190806083642  root    76.20.69.183
20190806121535  root    177.184.189.209
20190806121544  root    177.184.189.209
20190806215749  root    113.215.193.142
20190806234122  root    91.250.22.133
20190807000655  root    188.92.75.248
20190807000854  root    188.92.75.248
20190807000859  root    188.92.75.248
20190807000906  root    188.92.75.248
20190807000910  root    188.92.75.248
20190807000914  root    188.92.75.248
20190807000929  root    188.92.75.248
20190807000932  root    188.92.75.248
20190807000936  root    188.92.75.248
20190807000940  root    188.92.75.248
20190807000944  root    188.92.75.248
20190807000949  root    188.92.75.248
20190807000958  root    188.92.75.248
20190807001003  root    188.92.75.248
20190807001007  root    188.92.75.248
20190807001026  root    188.92.75.248
20190807001029  root    188.92.75.248
20190807001034  root    188.92.75.248
20190807001037  root    188.92.75.248
20190807001046  root    188.92.75.248
20190807001050  root    188.92.75.248
20190807030112  root    49.69.83.42
20190807071353  root    125.42.179.246
20190807092650  root    49.130.34.61
20190807113440  root    115.213.128.113
20190807204659  root    115.59.120.27
20190808015054  root    124.91.188.127
20190808025740  root    114.236.111.192
20190808051431  root    121.234.62.143
20190808125819  root    180.69.95.125
20190808131159  root    73.153.145.9
20190808223543  root    182.86.99.103
20190809100145  root    14.145.21.169
20190809113933  root    60.184.127.149
20190810021324  root    114.236.205.156
20190810080309  root    118.112.91.138
20190810155600  root    113.122.54.85
20190810161007  root    36.26.115.174
20190810180254  root    183.191.223.60
20190810184841  root    27.44.240.37
20190810190739  root    27.10.230.41
20190811045451  root    79.105.214.148
20190811164947  root    222.140.18.37
20190811185757  root    1.28.3.152
20190811201904  root    118.112.91.138
20190812012034  root    39.187.73.53
20190812092544  root    61.147.42.240
20190812174217  root    168.205.194.23
20190813005553  root    220.141.144.17
20190813054308  root    182.243.121.39
20190813144706  root    221.223.95.29
20190813145320  root    115.209.80.158
20190813204448  root    178.248.87.113
20190814022117  root    114.248.106.156
20190814062028  root    122.189.222.238
20190814065936  root    178.69.74.35
20190814081613  root    112.123.68.214
20190814182723  root    171.123.196.194
20190814233809  root    27.211.110.13
20190815010116  root    114.91.131.79
20190815023707  root    114.217.214.92
20190815030819  root    106.111.72.217
20190815040938  root    118.168.131.152
20190815093608  root    193.189.87.216
20190815130927  root    190.142.211.194
20190815143204  root    67.182.89.30
20190815155723  root    95.238.21.47
20190815202302  root    36.251.149.219
20190816030555  root    1.170.18.101
20190816054335  root    50.98.130.158
20190816122130  root    115.216.39.37
20190816212806  root    36.248.182.119
20190817055516  root    95.81.121.121
20190817055527  root    95.81.121.121
20190817063533  root    39.82.165.124
20190817083816  root    103.119.82.220
20190817094555  root    46.150.230.54
20190817100717  root    36.230.99.91
20190817155145  root    36.230.99.91
20190817203241  root    42.239.176.213
20190818021743  root    49.70.151.179
20190818163134  root    182.119.156.35
20190819024959  root    125.114.22.221
20190819194308  root    113.91.169.110
20190820090542  root    182.119.154.243
20190820162334  root    112.246.50.25
20190820195338  root    27.129.238.107
20190820201501  root    106.118.29.177
20190820235332  root    175.43.163.84
20190821013511  root    125.109.141.104
20190821041219  root    45.7.108.95
20190821051739  root    114.253.120.188
20190821071459  root    222.78.76.80
20190821144648  root    175.166.225.213
20190821151246  root    153.34.196.52
20190821203441  root    58.55.40.180
20190821232227  root    37.255.15.124
20190822125701  root    113.229.70.29
20190822184017  root    115.59.161.203
20190823014523  root    111.59.184.154
20190823030850  root    14.117.245.25
20190823084328  root    124.230.246.25
20190824005329  root    110.88.116.49
20190824051808  root    46.159.26.118
20190824085148  root    183.184.1.44
20190824114000  root    112.242.187.85
20190824211956  root    1.252.42.102
20190824231103  root    221.231.49.178
20190825012547  root    36.106.167.197
20190825071308  root    223.241.254.22
20190825135313  root    113.215.223.108
20190825171011  root    171.117.148.213
20190826061317  root    119.165.192.72
20190826074303  root    149.147.186.20
20190826074312  root    149.147.186.20
20190826091634  root    168.90.143.166
#

ここまでデータを整理すれば、あとはスプレッドシートアプリケーションに流し込んで処理することもできる。

データをスプレットシートアプリケーションに流し込んだサンプル

コマンドやシェルスクリプトの扱いに慣れてくれば、このくらいのシェルスクリプトなら1、2分で作れるようになる。「ログファイルからデータを取り出す作業を手動で」なんて、とてもじゃないが面倒でやっていられない。シェルスクリプトを使えば、こんな感じでサクッと処理することができる。知っているのと知っていないのとでは大きな違いだろう。一度自動化してしまえば、業務がぐっと楽になる。ぜひともいろいろな処理の自動化に挑戦してもらいたい。