omuronの備忘録

個人的な備忘録

「JAWS-UGコンテナ支部 #17 ECS/Fargate PV 1.4 ローンチ記念!」 #jawsug_ct 受講

今日は「JAWS-UGコンテナ支部 #17 ECS/Fargate PV 1.4 ローンチ記念!」見てました。

jawsug-container.connpass.com

ハッシュタグ#jawsug_ct

ハマコーさんのワッショーイからスタート。

コンテナとコンテナのつなぎかた on ECS

ECS の連携

  • ESC Task 内接続
    • サイドカー、アンバサダー、アダプターとか
    • 機能別コンテナをつくってアプリコンテナを純粋に保つ
    • ネットワークやストレージはホストで共有
  • ECS Service 間接続
    • ログ用のコンテナを分けちゃうとか
    • ドメインと関係ないコンテナを外に出す
  • ECS Service イベント駆動連携
    • インテグレーション時に問題が発生するとか、範囲が大きく調整に時間がかかるなら分けちゃう
    • コンテナ内部の実装に暗黙的に依存はしないとき
    • インテグレーションの範囲が絞れてないとき

ECS Service Discovery

  • サービス同士をAPIでつなぐ
  • ヘルスチェックが通ったらサービス登録
  • AWS Cloud Map に登録して、Route53 に反映され、DNS経由でAPIへ接続

ほかのサービス関連系

  • AWS App Mesh によるサービス間通信レイヤーの作成
  • Amazon EventBridge もしくは SNS&SQS での連携
  • AWS Step Functions で整理
  • AWS Cloud Map か AWS App Mesh どっちがいいか比べる

このあたりを使ったことないので、試さないと勘所がつかめなさそう。

金融系サービスでECS/Fargateを設計するということ

speakerdeck.com

Fintech系だとどこまで気にしないとダメなのか...
すごい学びになったが通常のプロダクトだと手を抜いてしまい、そこまで気を回せる気がしなかった。

FargateでService、Cron、RunTask基盤を運用する

speakerdeck.com

元プロゲーマーでアジアチャンピオン!

ホスティングサービスのインフラ環境を再構築!〜AWS Fargateのおかげで幸せになれた話〜

speakerdeck.com

Fargate を最初から使うのは辛いと。
EC2 タイプも起動しておいて、EC2 へ SSH ログインしながら運用したりすると。

所感

ECS をゼロから構築したこと無いので、やってみないとピンと来ないことが多かった。
Docker はローカルで普段使いしてある程度慣れてきたので、そろそろ ECS の構築もやっていきたい。

aws-cli を Docker で起動するように変更

OpenSSL のインストールとか、 brew で色々触ってたら、 aws コマンドが呼び出せなくなってしまった。
再インストールとか環境構築めんどくさいから、Docker で動かそうとしたら普通に公式に記事がありました。

docs.aws.amazon.com

.bash_profile に追加しておこう。

alias aws='docker run --rm -it -v ~/.aws:/root/.aws -v $(pwd):/aws amazon/aws-cli'

Auth0 の JWT 検証デバッグ sha256 署名編

JWT 検証デバッグ昨日 の続き。
署名が正しいか自力で検証する。

openssl 準備

macOSopenssl コマンドを確認する。

$ openssl version
LibreSSL 2.6.5

OpenSSL が欲しいので brew でいれる。

$ brew instal openssl

... 適当にパスを通して

$ openssl version
OpenSSL 1.1.1g  21 Apr 2020

検証する JWT

テスト用なので見られても困らないので、 token もここで公開します。
これを利用して検証を行う。

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJmNGJWZXlRZWFTMVNieDkxWXVLaSJ9.eyJpc3MiOiJodHRwczovL29tdXJvbi51cy5hdXRoMC5jb20vIiwic3ViIjoiaWV5ZGt1M0Q3QVRDbm5BN2ZHaFJ5anZobjQ0NVNmQ1NAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vb211cm9uLnVzLmF1dGgwLmNvbS9hcGkvdjIvIiwiaWF0IjoxNTkyNTYzMTU3LCJleHAiOjE1OTI2NDk1NTcsImF6cCI6ImlleWRrdTNEN0FUQ25uQTdmR2hSeWp2aG40NDVTZkNTIiwic2NvcGUiOiJyZWFkOmNsaWVudF9ncmFudHMgY3JlYXRlOmNsaWVudF9ncmFudHMgZGVsZXRlOmNsaWVudF9ncmFudHMgdXBkYXRlOmNsaWVudF9ncmFudHMgcmVhZDp1c2VycyB1cGRhdGU6dXNlcnMgZGVsZXRlOnVzZXJzIGNyZWF0ZTp1c2VycyByZWFkOnVzZXJzX2FwcF9tZXRhZGF0YSB1cGRhdGU6dXNlcnNfYXBwX21ldGFkYXRhIGRlbGV0ZTp1c2Vyc19hcHBfbWV0YWRhdGEgY3JlYXRlOnVzZXJzX2FwcF9tZXRhZGF0YSByZWFkOnVzZXJfY3VzdG9tX2Jsb2NrcyBjcmVhdGU6dXNlcl9jdXN0b21fYmxvY2tzIGRlbGV0ZTp1c2VyX2N1c3RvbV9ibG9ja3MgY3JlYXRlOnVzZXJfdGlja2V0cyByZWFkOmNsaWVudHMgdXBkYXRlOmNsaWVudHMgZGVsZXRlOmNsaWVudHMgY3JlYXRlOmNsaWVudHMgcmVhZDpjbGllbnRfa2V5cyB1cGRhdGU6Y2xpZW50X2tleXMgZGVsZXRlOmNsaWVudF9rZXlzIGNyZWF0ZTpjbGllbnRfa2V5cyByZWFkOmNvbm5lY3Rpb25zIHVwZGF0ZTpjb25uZWN0aW9ucyBkZWxldGU6Y29ubmVjdGlvbnMgY3JlYXRlOmNvbm5lY3Rpb25zIHJlYWQ6cmVzb3VyY2Vfc2VydmVycyB1cGRhdGU6cmVzb3VyY2Vfc2VydmVycyBkZWxldGU6cmVzb3VyY2Vfc2VydmVycyBjcmVhdGU6cmVzb3VyY2Vfc2VydmVycyByZWFkOmRldmljZV9jcmVkZW50aWFscyB1cGRhdGU6ZGV2aWNlX2NyZWRlbnRpYWxzIGRlbGV0ZTpkZXZpY2VfY3JlZGVudGlhbHMgY3JlYXRlOmRldmljZV9jcmVkZW50aWFscyByZWFkOnJ1bGVzIHVwZGF0ZTpydWxlcyBkZWxldGU6cnVsZXMgY3JlYXRlOnJ1bGVzIHJlYWQ6cnVsZXNfY29uZmlncyB1cGRhdGU6cnVsZXNfY29uZmlncyBkZWxldGU6cnVsZXNfY29uZmlncyByZWFkOmhvb2tzIHVwZGF0ZTpob29rcyBkZWxldGU6aG9va3MgY3JlYXRlOmhvb2tzIHJlYWQ6YWN0aW9ucyB1cGRhdGU6YWN0aW9ucyBkZWxldGU6YWN0aW9ucyBjcmVhdGU6YWN0aW9ucyByZWFkOmVtYWlsX3Byb3ZpZGVyIHVwZGF0ZTplbWFpbF9wcm92aWRlciBkZWxldGU6ZW1haWxfcHJvdmlkZXIgY3JlYXRlOmVtYWlsX3Byb3ZpZGVyIGJsYWNrbGlzdDp0b2tlbnMgcmVhZDpzdGF0cyByZWFkOnRlbmFudF9zZXR0aW5ncyB1cGRhdGU6dGVuYW50X3NldHRpbmdzIHJlYWQ6bG9ncyByZWFkOnNoaWVsZHMgY3JlYXRlOnNoaWVsZHMgdXBkYXRlOnNoaWVsZHMgZGVsZXRlOnNoaWVsZHMgcmVhZDphbm9tYWx5X2Jsb2NrcyBkZWxldGU6YW5vbWFseV9ibG9ja3MgdXBkYXRlOnRyaWdnZXJzIHJlYWQ6dHJpZ2dlcnMgcmVhZDpncmFudHMgZGVsZXRlOmdyYW50cyByZWFkOmd1YXJkaWFuX2ZhY3RvcnMgdXBkYXRlOmd1YXJkaWFuX2ZhY3RvcnMgcmVhZDpndWFyZGlhbl9lbnJvbGxtZW50cyBkZWxldGU6Z3VhcmRpYW5fZW5yb2xsbWVudHMgY3JlYXRlOmd1YXJkaWFuX2Vucm9sbG1lbnRfdGlja2V0cyByZWFkOnVzZXJfaWRwX3Rva2VucyBjcmVhdGU6cGFzc3dvcmRzX2NoZWNraW5nX2pvYiBkZWxldGU6cGFzc3dvcmRzX2NoZWNraW5nX2pvYiByZWFkOmN1c3RvbV9kb21haW5zIGRlbGV0ZTpjdXN0b21fZG9tYWlucyBjcmVhdGU6Y3VzdG9tX2RvbWFpbnMgdXBkYXRlOmN1c3RvbV9kb21haW5zIHJlYWQ6ZW1haWxfdGVtcGxhdGVzIGNyZWF0ZTplbWFpbF90ZW1wbGF0ZXMgdXBkYXRlOmVtYWlsX3RlbXBsYXRlcyByZWFkOm1mYV9wb2xpY2llcyB1cGRhdGU6bWZhX3BvbGljaWVzIHJlYWQ6cm9sZXMgY3JlYXRlOnJvbGVzIGRlbGV0ZTpyb2xlcyB1cGRhdGU6cm9sZXMgcmVhZDpwcm9tcHRzIHVwZGF0ZTpwcm9tcHRzIHJlYWQ6YnJhbmRpbmcgdXBkYXRlOmJyYW5kaW5nIGRlbGV0ZTpicmFuZGluZyByZWFkOmxvZ19zdHJlYW1zIGNyZWF0ZTpsb2dfc3RyZWFtcyBkZWxldGU6bG9nX3N0cmVhbXMgdXBkYXRlOmxvZ19zdHJlYW1zIGNyZWF0ZTpzaWduaW5nX2tleXMgcmVhZDpzaWduaW5nX2tleXMgdXBkYXRlOnNpZ25pbmdfa2V5cyByZWFkOmxpbWl0cyB1cGRhdGU6bGltaXRzIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.TJyTQQpBq0j6X-Smq8qcNkChbBxAFJT_xFNn8Bc2Uv-UP2KZsGRq801bLKdTgNUz2Wp8FT15i4LHCDwv1qhxpgMN9LdlL3uqNmzFgFcWXHnsToARf9mV3Hyi_QYZSzugrzql4jEZyITOhaxelThcko4FVZ4rL6-Tc4ZoZlkgY_6PgPlRrC5DXZR74-iVoruydPlo3lCnJmQkx4jcNCtqYXXpQsKCjCxoDD_Jzb_x16RMUczxrBqNuo6VSVjsiyPmkisdpsCzgwH8xZIK8dGJ_lDtV26GSOui6Gg9dN5smVGHBuDuaP_WYvAEh8qCkxFE5Z6WOrvvnyvGgNzRquuajA

公開鍵保存

昨日取得した公開鍵を pem ファイルに保存しておく。
pub のほうが名前としては正しいかもだけど)

$ echo "-----BEGIN CERTIFICATE-----
MIIDATCCAemgAwIBAgIJbOX8RffC+u+LMA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNVBAMTE29tdXJvbi51cy5hdXRoMC5jb20wHhcNMjAwNjE5MDgxMjAwWhcNMzQwMjI2MDgxMjAwWjAeMRwwGgYDVQQDExNvbXVyb24udXMuYXV0aDAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5HgBZMeT0iwu24jS9KsDQX85GLwU9kdbLYnuq/IT8LFENrUH7WxUhRaIaS3FvA4T/OCVNdVcJ+nPH++Gq2AlW4rkv+BBp963IICj9dwYbwZFFKeSlUMp2/QYdg2HeBZLJpqM8z6MhC8tejvWfAZ7Pblu3hICP5wyByDzjF9BiDDfrxdugfUGP0RtRe9EK1+51ZhByO8Hss4mIgoyrDOaw6R0vxJYxJCivOpvhhaKuSvEr+rA4tvOEkd3yeC5JqMyzLb/wouF9Md+0foxpzOrgoYnlIR02zcgnzlMHyRbV5Soaj1nokeXhMmIxU5d8BGNgSn+KX3lIexEHj5XchUUUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSSScOdNOvjZ/oiJTaKoRk21MXjVzAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADggEBAIKHXiXfdzB7IS2N2oeI2519aoFukgx5gzfeG7lecjFDGo4CVxt8ez1/oF9ZZeIopZ150zEtkWOuU4C0IvkKNqT5MyKBfW38ED23SPXuuuPdqXcmlsL7AaKpNYuFTAE7escc1yJ2ddVGWRh+F+jeyAii+cP+5LxDyz39p3wBTeB9Am0RiXnNG+nXkPtT/y/MJ7H7jqjoK5zUHcveLRvxfp2K2RDpZtzDV2u0L+SzsPPgi27VCaCKchZpih2I9SDvhXVMKXLWm5mir8triXBjfYIYcSOz/UqzgpxJp6H9wzyaIfrxBMnMWsMF82movhwINV9bSXl475FpLVvEcUvMwp0=
-----END CERTIFICATE-----" > pem

JWT の署名を取り出す

JWT の3つめのセクションが、署名になる。
base64 デコードが必要なので、4byte パディングの調整を = で行い、情報を確認。

$ echo "TJyTQQpBq0j6X-Smq8qcNkChbBxAFJT_xFNn8Bc2Uv-UP2KZsGRq801bLKdTgNUz2Wp8FT15i4LHCDwv1qhxpgMN9LdlL3uqNmzFgFcWXHnsToARf9mV3Hyi_QYZSzugrzql4jEZyITOhaxelThcko4FVZ4rL6-Tc4ZoZlkgY_6PgPlRrC5DXZR74-iVoruydPlo3lCnJmQkx4jcNCtqYXXpQsKCjCxoDD_Jzb_x16RMUczxrBqNuo6VSVjsiyPmkisdpsCzgwH8xZIK8dGJ_lDtV26GSOui6Gg9dN5smVGHBuDuaP_WYvAEh8qCkxFE5Z6WOrvvnyvGgNzRquuajA==" | base64 -D | hexdump -C
00000000  4c 9c 93 41 0a 41 ab 48  fa 5f e4 a6 ab ca 9c 36  |L..A.A.H._.....6|
00000010  40 a1 6c 1c 40 14 94 ff  c4 53 67 f0 17 36 52 ff  |@.l.@....Sg..6R.|
00000020  94 3f 62 99 b0 64 6a f3  4d 5b 2c a7 53 80 d5 33  |.?b..dj.M[,.S..3|
00000030  d9 6a 7c 15 3d 79 8b 82  c7 08 3c 2f d6 a8 71 a6  |.j|.=y....</..q.|
00000040  03 0d f4 b7 65 2f 7b aa  36 6c c5 80 57 16 5c 79  |....e/{.6l..W.\y|
00000050  ec 4e 80 11 7f d9 95 dc  7c a2 fd 06 19 4b 3b a0  |.N......|....K;.|
00000060  af 3a a5 e2 31 19 c8 84  ce 85 ac 5e 95 38 5c 92  |.:..1......^.8\.|
00000070  8e 05 55 9e 2b 2f af 93  73 86 68 66 59 20 63 fe  |..U.+/..s.hfY c.|
00000080  8f 80 f9 51 ac 2e 43 5d  94 7b e3 e8 95 a2 bb b2  |...Q..C].{......|
00000090  74 f9 68 de 50 a7 26 64  24 c7 88 dc 34 2b 6a 61  |t.h.P.&d$...4+ja|
000000a0  75 e9 42 c2 82 8c 2c 68  0c 3f c9 cd bf f1 d7 a4  |u.B...,h.?......|
000000b0  4c 51 cc f1 ac 1a 8d ba  8e 95 49 58 ec 8b 23 e6  |LQ........IX..#.|
000000c0  92 2b 1d a6 c0 b3 83 01  fc c5 92 0a f1 d1 89 fe  |.+..............|
000000d0  50 ed 57 6e 86 48 eb a2  e8 68 3d 74 de 6c 99 51  |P.Wn.H...h=t.l.Q|
000000e0  87 06 e0 ee 68 ff d6 62  f0 04 87 ca 82 93 11 44  |....h..b.......D|
000000f0  e5 9e 96 3a bb ef 9f 2b  c6 80 dc d1 aa eb 9a 8c  |...:...+........|
00000100

この内容を jwt.sign に保存する。

$ echo "TJyTQQpBq0j6X-Smq8qcNkChbBxAFJT_xFNn8Bc2Uv-UP2KZsGRq801bLKdTgNUz2Wp8FT15i4LHCDwv1qhxpgMN9LdlL3uqNmzFgFcWXHnsToARf9mV3Hyi_QYZSzugrzql4jEZyITOhaxelThcko4FVZ4rL6-Tc4ZoZlkgY_6PgPlRrC5DXZR74-iVoruydPlo3lCnJmQkx4jcNCtqYXXpQsKCjCxoDD_Jzb_x16RMUczxrBqNuo6VSVjsiyPmkisdpsCzgwH8xZIK8dGJ_lDtV26GSOui6Gg9dN5smVGHBuDuaP_WYvAEh8qCkxFE5Z6WOrvvnyvGgNzRquuajA==" | base64 -D > jwt.sign

OpenSSL でハッシュ値確認

$ openssl rsautl -verify -asn1parse -in jwt.sign -certin -inkey pem
    0:d=0  hl=2 l=  49 cons: SEQUENCE          
    2:d=1  hl=2 l=  13 cons:  SEQUENCE          
    4:d=2  hl=2 l=   9 prim:   OBJECT            :sha256
   15:d=2  hl=2 l=   0 prim:   NULL              
   17:d=1  hl=2 l=  32 prim:  OCTET STRING      
      0000 - 30 55 55 70 f7 d2 4e c4-35 91 de 57 70 b7 4a c5   0UUp..N.5..Wp.J.
      0010 - ab 26 62 d5 58 23 d1 59-3c 3f 31 95 be 1b 35 9e   .&b.X#.Y<?1...5.

最後の2行のハッシュ値が、SHA256の出力結果であることが確認できる。

SHA256 ハッシュの検証

JWT の 「ヘッダー + . + ペイロード」と同じ値か、 shasum -a 256 コマンドを利用して検証を行う。

echo -n "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJmNGJWZXlRZWFTMVNieDkxWXVLaSJ9.eyJpc3MiOiJodHRwczovL29tdXJvbi51cy5hdXRoMC5jb20vIiwic3ViIjoiaWV5ZGt1M0Q3QVRDbm5BN2ZHaFJ5anZobjQ0NVNmQ1NAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vb211cm9uLnVzLmF1dGgwLmNvbS9hcGkvdjIvIiwiaWF0IjoxNTkyNTYzMTU3LCJleHAiOjE1OTI2NDk1NTcsImF6cCI6ImlleWRrdTNEN0FUQ25uQTdmR2hSeWp2aG40NDVTZkNTIiwic2NvcGUiOiJyZWFkOmNsaWVudF9ncmFudHMgY3JlYXRlOmNsaWVudF9ncmFudHMgZGVsZXRlOmNsaWVudF9ncmFudHMgdXBkYXRlOmNsaWVudF9ncmFudHMgcmVhZDp1c2VycyB1cGRhdGU6dXNlcnMgZGVsZXRlOnVzZXJzIGNyZWF0ZTp1c2VycyByZWFkOnVzZXJzX2FwcF9tZXRhZGF0YSB1cGRhdGU6dXNlcnNfYXBwX21ldGFkYXRhIGRlbGV0ZTp1c2Vyc19hcHBfbWV0YWRhdGEgY3JlYXRlOnVzZXJzX2FwcF9tZXRhZGF0YSByZWFkOnVzZXJfY3VzdG9tX2Jsb2NrcyBjcmVhdGU6dXNlcl9jdXN0b21fYmxvY2tzIGRlbGV0ZTp1c2VyX2N1c3RvbV9ibG9ja3MgY3JlYXRlOnVzZXJfdGlja2V0cyByZWFkOmNsaWVudHMgdXBkYXRlOmNsaWVudHMgZGVsZXRlOmNsaWVudHMgY3JlYXRlOmNsaWVudHMgcmVhZDpjbGllbnRfa2V5cyB1cGRhdGU6Y2xpZW50X2tleXMgZGVsZXRlOmNsaWVudF9rZXlzIGNyZWF0ZTpjbGllbnRfa2V5cyByZWFkOmNvbm5lY3Rpb25zIHVwZGF0ZTpjb25uZWN0aW9ucyBkZWxldGU6Y29ubmVjdGlvbnMgY3JlYXRlOmNvbm5lY3Rpb25zIHJlYWQ6cmVzb3VyY2Vfc2VydmVycyB1cGRhdGU6cmVzb3VyY2Vfc2VydmVycyBkZWxldGU6cmVzb3VyY2Vfc2VydmVycyBjcmVhdGU6cmVzb3VyY2Vfc2VydmVycyByZWFkOmRldmljZV9jcmVkZW50aWFscyB1cGRhdGU6ZGV2aWNlX2NyZWRlbnRpYWxzIGRlbGV0ZTpkZXZpY2VfY3JlZGVudGlhbHMgY3JlYXRlOmRldmljZV9jcmVkZW50aWFscyByZWFkOnJ1bGVzIHVwZGF0ZTpydWxlcyBkZWxldGU6cnVsZXMgY3JlYXRlOnJ1bGVzIHJlYWQ6cnVsZXNfY29uZmlncyB1cGRhdGU6cnVsZXNfY29uZmlncyBkZWxldGU6cnVsZXNfY29uZmlncyByZWFkOmhvb2tzIHVwZGF0ZTpob29rcyBkZWxldGU6aG9va3MgY3JlYXRlOmhvb2tzIHJlYWQ6YWN0aW9ucyB1cGRhdGU6YWN0aW9ucyBkZWxldGU6YWN0aW9ucyBjcmVhdGU6YWN0aW9ucyByZWFkOmVtYWlsX3Byb3ZpZGVyIHVwZGF0ZTplbWFpbF9wcm92aWRlciBkZWxldGU6ZW1haWxfcHJvdmlkZXIgY3JlYXRlOmVtYWlsX3Byb3ZpZGVyIGJsYWNrbGlzdDp0b2tlbnMgcmVhZDpzdGF0cyByZWFkOnRlbmFudF9zZXR0aW5ncyB1cGRhdGU6dGVuYW50X3NldHRpbmdzIHJlYWQ6bG9ncyByZWFkOnNoaWVsZHMgY3JlYXRlOnNoaWVsZHMgdXBkYXRlOnNoaWVsZHMgZGVsZXRlOnNoaWVsZHMgcmVhZDphbm9tYWx5X2Jsb2NrcyBkZWxldGU6YW5vbWFseV9ibG9ja3MgdXBkYXRlOnRyaWdnZXJzIHJlYWQ6dHJpZ2dlcnMgcmVhZDpncmFudHMgZGVsZXRlOmdyYW50cyByZWFkOmd1YXJkaWFuX2ZhY3RvcnMgdXBkYXRlOmd1YXJkaWFuX2ZhY3RvcnMgcmVhZDpndWFyZGlhbl9lbnJvbGxtZW50cyBkZWxldGU6Z3VhcmRpYW5fZW5yb2xsbWVudHMgY3JlYXRlOmd1YXJkaWFuX2Vucm9sbG1lbnRfdGlja2V0cyByZWFkOnVzZXJfaWRwX3Rva2VucyBjcmVhdGU6cGFzc3dvcmRzX2NoZWNraW5nX2pvYiBkZWxldGU6cGFzc3dvcmRzX2NoZWNraW5nX2pvYiByZWFkOmN1c3RvbV9kb21haW5zIGRlbGV0ZTpjdXN0b21fZG9tYWlucyBjcmVhdGU6Y3VzdG9tX2RvbWFpbnMgdXBkYXRlOmN1c3RvbV9kb21haW5zIHJlYWQ6ZW1haWxfdGVtcGxhdGVzIGNyZWF0ZTplbWFpbF90ZW1wbGF0ZXMgdXBkYXRlOmVtYWlsX3RlbXBsYXRlcyByZWFkOm1mYV9wb2xpY2llcyB1cGRhdGU6bWZhX3BvbGljaWVzIHJlYWQ6cm9sZXMgY3JlYXRlOnJvbGVzIGRlbGV0ZTpyb2xlcyB1cGRhdGU6cm9sZXMgcmVhZDpwcm9tcHRzIHVwZGF0ZTpwcm9tcHRzIHJlYWQ6YnJhbmRpbmcgdXBkYXRlOmJyYW5kaW5nIGRlbGV0ZTpicmFuZGluZyByZWFkOmxvZ19zdHJlYW1zIGNyZWF0ZTpsb2dfc3RyZWFtcyBkZWxldGU6bG9nX3N0cmVhbXMgdXBkYXRlOmxvZ19zdHJlYW1zIGNyZWF0ZTpzaWduaW5nX2tleXMgcmVhZDpzaWduaW5nX2tleXMgdXBkYXRlOnNpZ25pbmdfa2V5cyByZWFkOmxpbWl0cyB1cGRhdGU6bGltaXRzIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0" | shasum -a 256
30555570f7d24ec43591de5770b74ac5ab2662d55823d1593c3f3195be1b359e  -

30555570f7d24ec43591de5770b74ac5ab2662d55823d1593c3f3195be1b359e の値が、 openssl rsautl と同じ値であることが確認できる。
これを用いて、JWT が改ざんされていないか検証している。

以上

やっと、やっっと自力で JWT の署名が検証できた!

Auth0 の JWT 検証デバッグ

JWT 検証の大まかな動きは 昨日 の調べたのですが、もうちょっとちゃんと理解したい。
オーソライザーのサンプルデバッグして、もうちょっと理解する。 テスト用の Auth0 アカウントを omuron で取得して試してみる。

環境構築

Fork したサンプルソースを取得。

$ git clone https://github.com/omuron/auth0-golang-api-samples.git

環境変数設定。

始めから準備されている APIs を利用して設定。

f:id:omron:20200619193129p:plain

この設定をクローンしたソースで設定する。

$ cd 01-Authorization-RS256/
$ cp .env.example .env
$ vi .env

先の情報から、 .env の内容は以下の通りとなる。

AUTH0_DOMAIN=omuron.us.auth0.com
AUTH0_AUDIENCE=https://omuron.us.auth0.com/api/v2/

サンプル起動

docker で動かすシェルが準備されいるので起動する。

$ chmod +x exec.sh
$ ./exec.sh 

サンプル呼び出し

curl コマンドで試す。

public API

パブリックエンドポイントはそのまま呼べる。

$ curl -X GET http://localhost:3010/api/public
{"message":"Hello from a public endpoint! You don't need to be authenticated to see this."

private API

プライベートエンドポイントは、そのまま呼ぶと認証エラーになる

$ curl -X GET http://localhost:3010/api/private
Required authorization token not found

JWT を取得するために Auth0 Management APIAPI Explorer からアプリを作成して Token を簡単に取れるようにする。

f:id:omron:20200619193947p:plain

この TokenAuthorization: Bearer ヘッダーに付与して呼び出すと、プライベート API が呼び出せる。

$ curl -X GET http://localhost:3010/api/private -H "Authorization: Bearer ey..."
{"message":"Hello from a private endpoint! You need to be authenticated to see this."}

試しに適当に署名を改ざん(後ろ2byte削る)して渡すと認証エラーになる。

$ curl -X GET http://localhost:3010/api/private -H "Authorization: Bearer ey..."
crypto/rsa: verification error

認証処理

認証部分のソース。
jwtmiddleware というライブラリで、署名の検証を行っている。

 jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{
        ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
            // Verify 'aud' claim
            aud := os.Getenv("AUTH0_AUDIENCE")
            checkAud := token.Claims.(jwt.MapClaims).VerifyAudience(aud, false)
            if !checkAud {
                return token, errors.New("Invalid audience.")
            }
            // Verify 'iss' claim
            iss := "https://" + os.Getenv("AUTH0_DOMAIN") + "/"
            checkIss := token.Claims.(jwt.MapClaims).VerifyIssuer(iss, false)
            if !checkIss {
                return token, errors.New("Invalid issuer.")
            }

            cert, err := getPemCert(token)
            if err != nil {
                panic(err.Error())
            }

            result, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(cert))
            return result, nil
        },
        SigningMethod: jwt.SigningMethodRS256,
    })

クレームの検証

環境変数で指定した値と同一かチェックを行っている。

         // Verify 'aud' claim
            aud := os.Getenv("AUTH0_AUDIENCE")
            checkAud := token.Claims.(jwt.MapClaims).VerifyAudience(aud, false)
            if !checkAud {
                return token, errors.New("Invalid audience.")
            }
            // Verify 'iss' claim
            iss := "https://" + os.Getenv("AUTH0_DOMAIN") + "/"
            checkIss := token.Claims.(jwt.MapClaims).VerifyIssuer(iss, false)
            if !checkIss {
                return token, errors.New("Invalid issuer.")
            }

公開鍵取得

この getPemCert 内で、公開鍵を取得している。

         cert, err := getPemCert(token)
            if err != nil {
                panic(err.Error())
            }

getPemCert 内で、 jwks.json にアクセスして、公開鍵を生成する元ネタを取得。

 resp, err := http.Get("https://" + os.Getenv("AUTH0_DOMAIN") + "/.well-known/jwks.json")

cert に公開鍵がちゃんと生成されていることを確認できる。

-----BEGIN CERTIFICATE-----
MIIDATCCAemgAwIBAgIJbOX8RffC+u+LMA0GCSqGSIb3DQEBCwUAMB4xHDAaBgNVBAMTE29tdXJvbi51cy5hdXRoMC5jb20wHhcNMjAwNjE5MDgxMjAwWhcNMzQwMjI2MDgxMjAwWjAeMRwwGgYDVQQDExNvbXVyb24udXMuYXV0aDAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5HgBZMeT0iwu24jS9KsDQX85GLwU9kdbLYnuq/IT8LFENrUH7WxUhRaIaS3FvA4T/OCVNdVcJ+nPH++Gq2AlW4rkv+BBp963IICj9dwYbwZFFKeSlUMp2/QYdg2HeBZLJpqM8z6MhC8tejvWfAZ7Pblu3hICP5wyByDzjF9BiDDfrxdugfUGP0RtRe9EK1+51ZhByO8Hss4mIgoyrDOaw6R0vxJYxJCivOpvhhaKuSvEr+rA4tvOEkd3yeC5JqMyzLb/wouF9Md+0foxpzOrgoYnlIR02zcgnzlMHyRbV5Soaj1nokeXhMmIxU5d8BGNgSn+KX3lIexEHj5XchUUUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSSScOdNOvjZ/oiJTaKoRk21MXjVzAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADggEBAIKHXiXfdzB7IS2N2oeI2519aoFukgx5gzfeG7lecjFDGo4CVxt8ez1/oF9ZZeIopZ150zEtkWOuU4C0IvkKNqT5MyKBfW38ED23SPXuuuPdqXcmlsL7AaKpNYuFTAE7escc1yJ2ddVGWRh+F+jeyAii+cP+5LxDyz39p3wBTeB9Am0RiXnNG+nXkPtT/y/MJ7H7jqjoK5zUHcveLRvxfp2K2RDpZtzDV2u0L+SzsPPgi27VCaCKchZpih2I9SDvhXVMKXLWm5mir8triXBjfYIYcSOz/UqzgpxJp6H9wzyaIfrxBMnMWsMF82movhwINV9bSXl475FpLVvEcUvMwp0=
-----END CERTIFICATE-----

RSA 暗号化処理

公開鍵を使って、暗号化(実際は復号化?)している

         result, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(cert))
            return result, nil

先程の公開鍵から、暗号化に必要な ModulusExponent の値を取得。

28841510422262814191184117115823758213752086907513605263219924877674141560262837182643343501219794671606638745267994212071107711888444383578941981655498917109003582367242857637556293418425232866320959921520192272011454483771082546734210512600409639950379314126836230221764126757761049713194259208036603196548828825386256538323924518953230503729593844469047641753946128749417423643395982894203570868421081354315530158085347299958369526501387655729392972982576739942821990361267465203802890701650287459043817568473076772711628632764276314805392871972678852782737089350470516910831041844758005478192083860668206781109331 65537

この値を利用して、「電子署名」を復号化する。
復号化した値が、「ヘッダー + . + ペイロード」と同じか確認して、改ざんされてないかチェックを行う。
暗号化については このサイト が参考になった。

github.com/auth0/go-jwt-middleware を利用すれば、あまり深くわかってなくても処理ができる。

以上

最終的に署名を検証しているミドルウェアの中まではデバッグしてないけど、昨日よりは理解できた。

Auth0 と Cognito で JWT 検証を理解する

Cognito や Auth0 で、認証時に JWT の検証を行いますが、実際に何をしているかちゃんと理解できてません。 以前書いた Lambda オーソライザーで Auth0 のトークンを検証する で検証をすることはできました。 理解できなくても、SDK とかを利用して処理ができるのは、抽象化がちゃんとされている証拠ではあります。 改めて何をしているか少し確認してみます。

どうやって検証している?

さすが AWS 、良いドキュメントがありました。

docs.aws.amazon.com

理解したいのは「ステップ 2: JWT 署名を検証する」と「ステップ 3: クレームを検証する」部分です。

JWT 署名を検証する

ここの理解が怪しいので、間違っていれば有識者にツッコんでいただきたい。

公開鍵の取得

ヘッダーの情報をもとに、公開鍵を取得。
公開鍵の処理は、ヘッダーのアルゴリズムJSON ウェブキーセット (JWKS) を利用して、処理を行う。

Cognito : https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json  
Auth0: https://<Auth0テナント>.auth0.com/.well-known/jwks.json  

kid Claim と、 jwks.jsonkid が同じかチェック。
alg =RS256 が使われているので、ne をデコードした数値を使えば RSA の公開鍵は作成可能。
e は公開指数で、 AQAB が指定されてることが多く、デコードすると 65,537 になる。

base64の文字列: AQAB
base64変換:000000 010000 000000 000001
2進数へ:00000001 00000000 00000001
16進数へ:0x10001
10進数へ:65537

n は、RSA暗号のモジュラス(Modulus)で、2つの素数p, qの積...
ここの理解は諦めて RSA 暗号使ってるんだなということだけ把握して、 en から公開鍵を生成する。

注意点として、algnone に改ざんして検証を回避されないように脆弱性対応が必要。

Payload の暗号化 署名セクションの復号化

取得した公開鍵で、ヘッダーと Payload を暗号化する。
この値が、JWT の署名セクションと同じになるかを検証する。
非対称鍵だから、上記の方法ではなさそう。
勘違いしてたかもなので、再整理。

署名セクションを復号化する。
正確には「秘密鍵」で暗号化したものを「公開鍵」で復号化することはできないがニュアンスとしてはこうなるっぽい。
復号化した内容が、ヘッダーと Paload と同じであるか確認して、改ざんされてないかチェックを行う。

クレームを検証する

トークンの有効期限確認

JWT の Payload の exp Claim に入ってる値が未来かどうかチェック。
iat Claim を確認して、発行時間が改ざんされてないかも見たほうが良さそう。

対象者 (aud) のクレームの確認

aud Claim が、Client ID と同一かチェック。

発行者 (iss) のクレームの確認

以下の値と同一かチェック。

Cognito : `https://cognito-idp.<リージョン>.amazonaws.com/<userpoolID>`  
Auth0 : `https://<Auth0テナント>.auth0.com/` 

以上

この検証がすべて問題なければ、認証済みとみなす。
実際には、自分で実装することはほぼなく、SDK など公開されているサンプルを使うことのほうが多いと思います。

オーソライザーサンプル

Auth0 公式を参考に確認しました。

github.com

Developers.IO CONNECT 2020 引き続き眺める #devio2020

昨日 に引き続き、Developers.IO CONNECT 2020 眺めてます。

ケーススタディで学ぶ企業運営〜クラスメソッドの新型コロナ対応〜 #devio2020

Live で見なくてもすぐにブログでまとめられるだろうと思ってた通り、即公開されました。

dev.classmethod.jp

企業カルチャーを明文化しているのは強いですね。
キーワードは全員平等でいい意味で個人の事情を加味しない。
特別休暇5日間、テレワーク準備資金5万円全員配布、使途PS4...
社員プロフィールツールを作ったので公開予定とのことなのでこれは期待できる!
やってみる文化は、「通常社員向けであって企業向けでは適用できない」という一般的な印象があったのですが、会社運営でもやってみるというのはさすがです。

ALL AWS Certifications EngineerによるAWS認定全制覇への道 #devio2020

www.youtube.com

クラメソには12冠が7人もいると。
Alexa が個人的に一番つらかったと。この前のサバワでも一番つらいと言われてたけど、興味あるかどうかに尽きるところもありそう。
AWS が推奨する方法が答えになるから AWS Well-Architected を確認しておくこと。
「認定者ラウンジ」が特典で確かに嬉しいんですが、オフラインイベントがなくなってしまったので、しばらくこの特典が使えることがなさそうなのが寂しい。

CloudGoat で AWS セキュリティの世界に入門しよう #devio2020

www.youtube.com

CloudGoat 知りませんでした。

ハイトラフィック運用 〜スループットの向上を目指して〜 #devio2020

www.youtube.com

CloudWatch は避けて通れない道。
ダッシュボードにいっぱいグラフ並べて眺めるのは好きだけど、ログを検索して追いかけるのは苦手。
Container Insights でコンテナやタスク別の監視ができる。
DynamoDB のパーティションキーで分散される仕組みは、開発側で意識しなくてもそのうち改善されるのかな。昔の S3 もプレフィックスで分散を意識的にしないとだめだったけど改善されたし。
「チューニングに鋼の弾丸はない」。はい、メトリックスをちゃんと取って改善がんばります。

所感

Live でみると QA に参加できるメリットがありますが、QA もすべて公開されるしアーカイブで見ても今のところ困ってません。
Tiwtter とかで実況しながら参加できるのは楽しいんですが、業務時間と被るとなかなか生で見れないから、そこだけは我慢です。

Developers.IO CONNECT 2020 はじまりました #devio2020

今年の Developers.IO はオンラインです。

classmethod.jp

Live 以外はいつでも見れるのがオンラインのいいところ。
再生速度も調整できるので、少し速度を上げつつ、いくつか聞きました。

はじめてのCloudFormation #devio2020

www.youtube.com

CloudFormation とても好きです。
使うのが目的になったり、自己満足になりがちですが、IaC で一気にできるのは何度やっても楽しいですね。
いい復習になりました。
ドリフトの検出とか全然使わず存在すら忘れてた。

初心者だからこそ触りたい、AWS CLI ~ "躓きやすい"を無くしたい ~ #devio2020

www.youtube.com

たいがーさんが社会人になってクラメソにジョインしてます。
さすがプレゼン慣れしてて上手ですね。
学生の時から素晴らしい才能を発揮したけど、3年前の WordCamp で出会ったときは同じ業界になるとは思いませんでした。

新卒エンジニアが、入社最終面接に持って行ったポートフォリオWebAPIを、入社後に得た知見で見直してみる ~ サーバーレスのCI/CDやってみた ~ #devio2020

www.youtube.com

クラメソの新入社員は化物揃いか...
入社前ですでに AWS を存分に使ってます。

知らなくても困らないけど、知ると楽しいVPCの裏側の世界 #devio2020

www.youtube.com

re:Invent などでしか公開されないような裏側の仕組みの話です。
AWS を使うだけなら抽象化されてて知る必要もないですが、どのようなハードウェアや仕組みで動いているか知るのは面白いですね。

所感

聞きたいセッションが多い中、いつでも聞けるのは助かります。
物理的な Developers.IO の場合は、重複するセッションは聞くことができませんが、だいたいブログでフォローされるので知ることはできます。
今回も全部ブログになると思ってますが、やはり動画で見れるのはわかりやすくていいですね。