RDS Aurora 監査ログの確認

以下の条件の時の監査ログについて確認した時のメモ。

RDS Aurora の状態

  • Cluster の「監査ログ」にチェック済み
  • Cluster Parameter Group は以下の通り設定
    • server_audit_logging : 1
    • server_audit_logs_upload : 0
    • server_audit_events : CONNECT,QUERY_DDL,QUERY_DCL
    • server_audit_excl_users : rdsadmin,mysql.sys

確認のため実施した内容

  • 認証失敗
    • mysql クライアントで接続試行し、わざとパスワードを間違える
  • 認証成功
    • mysql クライアントで接続試行し、正しいパスワードを入れる
  • DDL, DCL の確認用 SQL
CREATE USER 'noo'@'%';
SET PASSWORD FOR 'noo'@'%' = 'hogehogehoge';
GRANT SELECT ON `*`.* TO 'noo'@'%' IDENTIFIED BY 'hogehogehoge';
DROP USER 'noo'@'%';
  • SELECT 等の DML クエリが記録されないことを確認するための SQL
SELECT * FROM mysql.user;

結果

1706705683981863,aurora-instance-01,admin,10.0.0.24,11759925,0,FAILED_CONNECT,,,1045
1706705683981894,aurora-instance-01,admin,10.0.0.24,11759925,0,DISCONNECT,,,0
1706705688849140,aurora-instance-01,admin,10.0.0.24,11759927,0,CONNECT,,,0
1706705716280568,aurora-instance-01,admin,10.0.0.24,11759927,340029750,QUERY,,'CREATE USER \'noo\'@\'%\' IDENTIFIED WITH \'mysql_native_password\'',0
1706705718993333,aurora-instance-01,admin,10.0.0.24,11759927,340029765,QUERY,,'SET PASSWORD FOR `noo`@`%`=<secret>',0
1706705721475686,aurora-instance-01,admin,10.0.0.24,11759927,340029785,QUERY,,'GRANT SELECT ON `*`.* TO \'noo\'@\'%\' IDENTIFIED WITH \'mysql_native_password\' AS \'<secret>\'',0
1706705724527190,aurora-instance-01,admin,10.0.0.24,11759927,340029813,QUERY,,'DROP USER \'noo\'@\'%\'',0
1706705754747281,aurora-instance-01,admin,10.0.0.24,11759927,0,DISCONNECT,,,0
  • 1行目: 接続失敗(FAILED_CONNECT)
  • 2行目: 切断(DISCONNECT)
  • 3行目: 接続成功(CONNECT)
  • 4〜7行目: 発行されたクエリのログ
    • パスワード部分は <secret> となってマスクされている
    • DROP USER の後に流した SELECT 文は記録されていない
  • 8行目: 切断(DISCONNECT)

OpenSearch (Elasticsearch) に CloudFront のアクセスログを連携するための Ingest Pipeline

OpenSearch (Elasticsearch) に CloudFront のアクセスログを連携するために必要な Ingest Pipeline についてのメモ。

Ingest Pipeline とは、Document を POST した時に、その Document を登録する前に加工することができる機能。
渡ってきた CloudFront のアクセスログをちょっと加工して、OpenSearch 上で扱いやすくする。

以下コマンド。

curl -H "Content-Type: application/json" -XPUT 'https://{domain}/_ingest/pipeline/cflog' -d '{
  "processors": [
    {
      "grok": {
        "field": "message",
        "pattern_definitions": {
          "CFDATETIME" : "\\d{4}-\\d{2}-\\d{2}\\t\\d{2}:\\d{2}:\\d{2}"
        },
        "patterns":[ "%{CFDATETIME:timestamp}\t%{NOTSPACE:edge_location}\t%{NOTSPACE:response_bytes}\t%{NOTSPACE:client_ip}\t%{NOTSPACE:http_method}\t%{NOTSPACE:distribution_host}\t%{NOTSPACE:http_path}\t%{NOTSPACE:http_status}\t%{NOTSPACE:http_referer}\t%{NOTSPACE:agent}\t%{NOTSPACE:query_strings}\t%{NOTSPACE:cookies}\t%{NOTSPACE:edge_result_type}\t%{NOTSPACE:edge_request_id}\t%{NOTSPACE:host_header}\t%{NOTSPACE:protocol}\t%{NOTSPACE:request_bytes}\t%{NOTSPACE:response_time:float}\t%{NOTSPACE:x_forwarded_for}\t%{NOTSPACE:tls_protocol}\t%{NOTSPACE:tls_cipher}\t%{NOTSPACE:edge_response_result_type}\t%{NOTSPACE:http_version}\t%{NOTSPACE:fle_status}\t%{NOTSPACE:fle_encrypted_fields}\t%{NOTSPACE:request_port}\t%{NOTSPACE:time_to_first_byte:float}\t%{NOTSPACE:edge_detailed_result_type}\t%{NOTSPACE:content_type}\t%{NOTSPACE:content_length}\t%{NOTSPACE:content_range_start}\t%{NOTSPACE:content_range_end}" ],
        "ignore_missing": true
      }
    },
    {
      "remove": {
        "field": "message"
      }
    },
    {
      "user_agent": {
        "field": "agent",
        "target_field": "user_agent",
        "ignore_failure": true
      }
    },
    {
      "remove": {
        "field": "agent",
        "ignore_failure": true
      }
    },
    {
      "gsub": {
        "field": "timestamp",
        "pattern": "(\\d{4}-\\d{2}-\\d{2})\\t(\\d{2}:\\d{2}:\\d{2})",
        "replacement": "$1T$2Z"
      }
    },
    {
      "date": {
        "field": "timestamp",
        "formats": [
          "date_time_no_millis"
        ]
      }
    }
  ]
}'

Ingest Pipeline は(恐らく)上から評価が行われる。
この設定について上から解説する。

1. grok processor

www.elastic.co

grok とは、定義済みの正規表現を使ってテキスト分割して指定した Field にマッピングするツール。
パターンはこの辺を参考に考えていく。
www.alibabacloud.com

また、CloudFront の標準ログは以下の定義を見ながら、パターンを考える。
docs.aws.amazon.com

grok processor では、最初に渡ってきた message という Field を、 patterns に従って各 Field に分割してマッピングするよう指示。

しかし、定義済みの正規表現の中に該当するものがないこともある。その場合、自分でカスタム正規表現を定義できる。 それが pattern_definitions である。

CloudFront の標準ログでは、日付と時間が別の項目として出力されているので、それを1つの timestamp という Field にマッピングするために CFDATETIME という正規表現を定義している。
それ以外は 面倒くさくて 特にこだわりは無いので、全て NOTSPACE (空白以外の文字列) で解析して各 Field にマッピングしている。

2. remove processor

www.elastic.co

grok processormessage Field を各 Filed に分割した後は不要になるので、 message Field を削除している。

3. user_agent processor

www.elastic.co

user_agent processor は指定した Field が User Agent であると認識させ、その User Agent を解析して更に詳細な Field にマッピングしてくれる。

4. remove processor

user_agent processor 処理後は agent Field そのものは不要になるので削除している。

5. gsub processor

www.elastic.co

grok でマッピングした timestamp に入っている文字列では、日付と時間がタブで区切られていて、そのままだと OpenSearch(Elasticsearch) 側に time field として認識してもらえない。
ISO8601 形式に変換するために、 timestampyyyy-MM-ddTHH:mm:ssZ 形式に変換している。

6. date processor

www.elastic.co

ISO8601 形式に変換した timestamp Field の文字列を date Field として認識させる。

Amazon OpenSearch Service でスナップショットリポジトリを登録する

AWS OpenSearch Service でスナップショットを新規に登録する場合、Kibana の Dev Tools からでは実行できない。
https://docs.aws.amazon.com/ja_jp/opensearch-service/latest/developerguide/managedomains-snapshots.html#managedomains-snapshot-register

上記ドキュメントに記載の Python サンプルコードを使って登録することで解決する。

import boto3
import requests
from requests_aws4auth import AWS4Auth

host = '' # domain endpoint with trailing /
region = '' # e.g. us-west-1
service = 'es'
credentials = boto3.Session().get_credentials()
awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service, session_token=credentials.token)

# Register repository

path = '_snapshot/my-snapshot-repo-name' # the OpenSearch API endpoint
url = host + path

payload = {
  "type": "s3",
  "settings": {
    "bucket": "s3-bucket-name",
    "region": "us-west-1",
    "role_arn": "arn:aws:iam::123456789012:role/snapshot-role"
  }
}

headers = {"Content-Type": "application/json"}

r = requests.put(url, auth=awsauth, json=payload, headers=headers)

print(r.status_code)
print(r.text)

サンプルコードを自分の環境用に修正したら、自分のローカルで実施しても良いが、今回は CloudShell でやってみた。
コマンドは以下の通り。

$ vi register-repo.py # サンプルコード配置
$ sudo pip3 install boto3 # boto3 インストール
$ sudo pip3 install requests_aws4auth # requests_aws4auth インストール
$ python3 register-repo.py # 実行
200
{"acknowledged":true}

AWS OpenSearch の Kibana ログインに IAM Identity Center (旧AWS SSO) を使う

完全に個人的なメモ。

  1. 対象の OpenSearch クラスターを表示
  2. 「セキュリティ設定」タブを表示し、「編集」ボタン押下
  3. OpenSearch Dashboards/Kibana 用の SAML 認証」欄の SAML 認証を有効化 にチェック
  4. 表示される以下の情報をコピー
    • サービスプロパイダエンティティ ID
    • IdP によって開始された SSO URL
  5. この画面を表示したまま別タブ等で IAM Identity Center を表示
  6. 「アプリケーション > 「アプリケーションを追加」ボタンを押下
  7. カスタム SAML 2.0 アプリケーションの追加 にチェックを入れ「次」ボタンを押下
  8. 「アプリケーションを設定」欄は任意に入力
  9. IAM Identity Center SAML メタデータファイル をダウンロード
  10. 「アプリケーションのプロパティ」欄はセッション時間のみ設定
  11. 「アプリケーションメタデータ」欄
    • メタデータ値をマニュアルで入力する にチェック
    • アプリケーション ACS URL には先程の IdP によって開始された SSO URL を入力
    • アプリケーション SAML 対象者 には先程の サービスプロパイダエンティティ ID を入力
  12. 「送信」ボタンを押下
  13. SSO アプリケーションの追加後、追加されたアプリケーションを選択
  14. 「アクション」 > 「属性マッピングを編集」を選択
  15. 以下の通り入力し、「変更の保存」ボタンを押下

    アプリケーションのユーザー属性 この文字列値または IAM Identity Center のユーザー属性にマッピング 形式
    Subject ${user:subject} unspecified
    Role ${user:groups} unspecified
    User ${user:name} unspecified
  16. 「ユーザーを割り当て」ボタンを押下し、ログインさせたいユーザーを割り当てる

参考

昔受けた面接で失礼に感じた話

自分は IT 業界にいるのだけど、「ユニコーンに乗って」というドラマがやっているのを見て、数年前にとあるスタートアップ企業の面接を受けてとても失礼だと思った話をふと思い出したので書いてみる。

その企業の理念や解決したい課題等に共感し、採用面接に応募。
面談や何度かの面接、体験入社を経て内定をいただくことができた。

問題はその後である。

何が失礼だと感じたか

それは、希望年収に全く届かない額の提示をしてきたことである。

具体的な額は伏せるが、希望年収の65%程度の提示額だったのだ。
仮に希望が600万円なら390万円とか、そういうレベルである。
希望年収は、その当時の現職の年収より低い額を設定していたにもかかわらず、だ。

なぜこの金額なのか質問してみたが、自社の基準にて経験や能力を総合的に判断してこの提示となったということだった。
ちなみに、この転職の時に内定をいただいた他の企業(4社ほど)では、希望年収の95〜110%程度を提示してもらっていたので、そもそもの希望年収が高望みすぎるということはなかったと思っている。

これのどこが失礼か

この企業の基準でそう評価されたのは別に良い。それは企業それぞれで良いのだから。
問題は、この提示額になるとどこかの段階で絶対わかっていたのに条件提示の面談までやらされたことである。

「希望年収が出せなくて心苦しいがオファーさせてくれ」という気持ちで他社のように95%とかの提示をいただくならまだわかる。
自分の希望年収は伝えていたはずで、(65%かどうかはわからずとも)最終的な額が希望より大幅に下回ることなんて途中の段階で絶対にわかっていただろう。
最終的な額が希望より大幅に下回るとわかった段階で、「うちでは希望年収を提示することは難しいから、申し訳ないが選考終了とさせてくれ」と連絡をくれれば良かったのだ。

それなのに最終面接までやらされ、さらには体験入社というイベントにも参加させられ、条件提示でこの顛末である。

こちらは面接をするために現職の予定を調整したり、業務終了後に訪問して面接を受けたりしていた。
体験入社では平日丸1日時間を取ってほしいと言われ、有給休暇も取得して時間を割いたのだ。

ここまで付き合わせておいて65%の提示。
しかもその提示額、募集要項に記載されていた最低年収に少し毛が生えた程度の額である。
こちとら、その当時で15年の業界経験値を持ち合わせておったが?

さすがにバカにしてると思った。

この件についての考察

なぜこのような事が起こったのか、自分の中では3つの仮説がある。

1つ目は、人材の市場価値を知らなすぎる可能性。
前述の通り、現職の年収や当時受けた他数社で希望年収の95〜110%程度を提示いただいたことから、当時の自分の市場価値は概ね認識通りだったと自負している。
しかし、件の企業の採用担当者や経営者は世間の感覚と著しくズレてしまっており、このような判断をしてしまったという仮設。

2つ目は、中途採用が買い手市場だと勘違いしていた可能性。
まぁこれも市場を知らなすぎるに近い話。
IT 業界はある時から人材流動性が高まってきていて、転職希望者は世の中にたくさん出てきている。
件の企業に中途の応募がたくさん来ていて、企業側が優位な立場だと勘違いしていたのではないかという仮設。

3つ目は、やりがい搾取する気満々だった可能性。
この企業は当時メディアに多数露出しており、話題性のある企業だった。
また、事業内容自体も魅力的で、やりがいは非常に高かったように思うし、自分もそう思ったから選考に進んだのだ。
それ故に、提示額がどんなに低くてもやりがいに釣られて入ってくるだろう、という驕りがあったのではないかという仮設。

3つ目が濃厚だと思っているが仮設の域は出ないので、結局どういうつもりでこのような提示をしてきたのかはわからない。

最後に

スタートアップ企業がお金が無いのはわかる。
わかるが、希望年収を大幅に下回る提示になるのなら選考を中断するべき。

この転職で受けた企業の中にはアーリーステージぐらいのスタートアップもあったが、その企業からは「採用できるかどうかは今仕掛けている資金調達次第」という生々しい話をしてくれた。
最終的には「資金調達に難航していて、xx月中に資金調達できそうにないから選考を中断させてほしい」と連絡をいただき、非常に誠実な気持ちを感じた。
(自分がxx月までには転職先を決めるつもりでいると話していたので)

自分も採用面接することもある人間なので気を付けよう。