インスタンスを立ち上げてから、みなさんに使ってもらうのは良いんですが、何を操作したかわからないのは、やばいですよね。
なので、Linuxの監査ログをカスタムログとして採取して管理しましょう。
OracleLinuxの場合、デフォルトでエージェントがセットアップされているので、簡単ですね。
ロギング-エージェント構成より、エージェント構成を作成します。
入力タイプ:ログパス
ファイルパス:/var/log/audit/audit.log
高度なパーサー・オブションで、AUDITDを指定することで、良い感じでJSONに変換してくれます。
最後にログの保存先を指定します。
これだけで、ログを吸い上げることが出来るんですが、一定期間で捨てられてしまいます。
なので、ObjectStorageにでも保管しておきましょう。
こういう時は、サービスコネクタを使うと出来るそうです。
どういうことかと言うと、OracleLinuxにインストールされているエージェントで、/var/log/audit/audit.log の中身をJSONに変換してロギングサービスに吸い上げます。
そして、サービスコネクタにより、その中身をObjectStorageへ一定間隔毎に出力します。
サービスコネクタのソースを/var/log/audit/audit.logを吸い上げたログにして、ターゲットをオブジェクトストレージにすると実現出来ます。
構築して、しばらくすると、ターゲットに指定したオブジェクトストレージにgz形式でファイルが作成されていきます。
後は、オブジェクトストレージにライフサイクルポリシーでも指定しておいて、自動削除すれば完成ですな。
あっ、ポリシーの設定を忘れてました。
allow any-user to manage objects in compartment hoge where all {request.principal.type= 'serviceconnector', request.principal.compartment.id='ocid1.compartment.oc1..aaaaaaaab2rjf3lkitnzo4tzax5ub2sec7lzwi7nfllpeo4xhogehoge'}
これだけで、とりあえず基本的なログを吸い上げれるのですが、ログの中身がいまいちです。
/var/log/audit/audit.log に、もっと色々な情報を詰め込みたいですよね。
そういう時は、auditctl を使います。
詳しいことは、Googleさんにでも尋ねて下さい。
auditctl で、色々追加するとログに記録されていくのですが、不揮発の設定にならないので、/etc/audit/rules.d/ に、定義ファイルを作成しておきましょうね。
以下のような定義を入れておくと、実行したコマンドが記録されていきます。
-a exit,always -F arch=b64 -F uid!=0 -F gid!=985 -S execve -k execute
これで、ObjectStorageへ記録されるのですが、この内容だけ分けてObjectStorageへ記録したい時は、サービスコネクタのログ・フィルタ・タスクで、data."body.key"='execute' を指定すると分けることが出来ます。
さて、コマンド実行の記録は取れたのですが、やっぱりキーログ的な物もほしいですか。
ということで、以下のファイルに追記してみましょう。
/etc/pam.d/system-auth
/etc/pam.d/password-auth
session required pam_tty_audit.so disable=* enable=*
これで、/var/log/audit/audit.log に、どんどん、キー操作が吸い上げられます。
中身は、バイナリエンコードされているので、見てもわかりにくいので、以下のコマンドで確認します。
aureport --tty
ということは、ObjectStorageに記録されている情報もわかりにくいですよね。
こんな時は、サービスコネクタで、ファンクション・タスクを使いデコードしちゃいましょう。
まずは、ファンクションを作成します。
import io
import json
import logging
import re
from fdk import response
# Decode tty output data
def decode_tty_data(org_data):
# ユーザーIDで分割する
hex_data=org_data.split('UID')[0][:-1]
# 16進数データをバイト配列に変換
bytes_data = bytes.fromhex(hex_data)
# バイト配列をASCII文字列にデコード
return bytes_data.decode('utf-8', errors='ignore')
# 制御コード置き換え
rep_dict = {
r"\x00":"",
r"\x04":"",
r"\x07":"<bel>",
r"\x08":"<bs>",
r"\x0a":"<lf>",
r"\x0d":"<cr>",
r"\x1b":"<esc>",
r"\x20":" ",
r"\n":"<ret>",
r"\t":"<tab>",
r"\x7f":"<del>",
}
def replace_control(srcstr):
replaced_string = srcstr
for k,v in rep_dict.items():
replaced_string = re.sub("[{0}]+".format(k),v,replaced_string)
return replaced_string
# エントリ
def handler(ctx, data: io.BytesIO = None):
instr = ""
try:
instr = data.getvalue().decode('utf-8')
# JSONオブジェクトを解析
body = json.loads(instr)
# 複数行対応
for rec in body:
#
rec["data"]["body.datadecode"] = replace_control(decode_tty_data(rec["data"]["body.data"]))
except (Exception, ValueError) as ex:
logging.getLogger().info(instr)
return str(ex)
return response.Response(
ctx, response_data=json.dumps(body, ensure_ascii=False),
headers={"Content-Type": "application/json"}
)
次に、キーログを分けます。
サービスコネクタのログ・フィルタ・タスクで、data."header.type"='TTY' を指定します。
さらにタスクの構成で、作成したファンクションを指定します。
後は、ターゲットを指定すればOkです。
あっ後、ポリシー
allow any-user to use functions-family in compartment hoge where all {request.principal.type='serviceconnector', request.principal.compartment.id='ocid1.compartment.oc1..aaaaaaaab2rjf3lkitnzo4tzax5ub2sec7lzwi7nfllpeo4xhogehoge'}
こんな感じで、キーログの中身も良い感じで取得出来るので、どんどん監査していきましょうよ