インスタンスを立ち上げてから、みなさんに使ってもらうのは良いんですが、何を操作したかわからないのは、やばいですよね。
なので、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 ioimport jsonimport loggingimport refrom fdk import response# Decode tty output datadef 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'}
こんな感じで、キーログの中身も良い感じで取得出来るので、どんどん監査していきましょうよ





