selmertsxの素振り日記

ひたすら日々の素振り内容を書き続けるだけの日記

OpenID Connectまとめ

概要

OpenID Connectは認証用のプロトコルである。 認可フローはOAuth 2.0そのままで、属性情報も渡せるようになる。 OpenID Connectを理解する上で、必要な用語は下記の通りある。

認証フロー

  • Authorization Code Flow
  • Implicit Flow

認証フローはHTTP requestのパラメータに追加することで指定することができる。

HTTP/1.1 302 Found
Location: https://op.example.domain/authorize?
response_type=code # <== ここ!!!
&scope=openid%20profile%20email
&client_id=XXXXXX
&state=abcdefg123
&redirect_uri=https%3A%2F%2Frp.example.domain%2Fcallback

Authorization Code Flow

認証手順

  • Client は必要なパラメータを含む Authentication Request を用意する.
  • Client は Authorization Server にリクエストを送信する.
  • Authorization Server は End-User を Authenticate する.
  • Authorization Server は End-User の Consent/Authorization を得る.
  • Authorization Server は Authorization Code を添えて End-User を Client に戻す.
  • Client は Token Endpoint へ Authorization Code を送信する.
  • Client は ID Token と Access Token をレスポンスボディに含むレスポンスを受け取る.
  • Client は ID Token を検証し, End-User の Subject Identifier を取得する.

ResourceProviderからUserInfoにアクセスするのが Implicit Flowとの大きな違い。

Implicit Flow

認証手順

  • Client は必要なパラメータを含む Authentication Request を用意する.
  • Client は Authorization Server にリクエストを送信する.
  • Authorization Server は End-User を Authenticate する.
  • Authorization Server は End-User の Consent/Authorization を得る.
  • Authorization Server は ID Token, および要求があれば Access Token を添えて End-User を Client に戻す.
  • Client は ID Token を検証し, End-User の Subject Identifier を取得する.

Authorization Code Flowとの大きな違いは下記の通り

  • ID TokenとAccess TokenがAuthorizationエンドポイントから返却される
  • ID Tokenの受け渡しはブラウザ上で動作するClientで行う
  • OpenID ProviderとRelying Partyの間で通信できない環境でも採用可能

http://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html#ImplicitFlowAuth

Implicit Flow は主にスクリプト言語を用いて実装されブラウザ上で動作する Client によって使用される. Access Token と ID Token は, Client に直接返却され, その Access Token と ID Token は, End-User と End-User の User Agent にアクセスするアプリケーションに露出する可能性がある. Authorization Server は Client Authentication を行わない.

ということで、SPAでやってるサービスなら、Implicit Flowを使う。

所感

OpenID Connectはユーザーに認証をさせる。 SAMLはサービスとIdPに認証をさせる。

OpenID Connectは、登録されるサービスの信頼性をIdPが確認しない。 自由に登録できるという点はメリットでもあり、デメリットでもある。

SAMLは登録するサービスをIdP側が完全に把握できる。 設定コストが高いという点ではデメリットでもある。

僕の読んだ資料

G SuiteをAWSのIDaaSとして使ってみたけど個人的にはイマイチだった

概要

G Suiteには Cloud Identity という IDaaSのサービスがあります。 G Suiteをすでに利用しているなら、こいつをIDaaSとして使えれば低コストでいけます。 なので、ちょっと試してみました。僕の認識不足な部分でイマイチな結論だしている部分があったらコメント貰えると嬉しいです。

support.google.com

TL; DR

  • 基本的には下記資料の手順に従って設定をした
  • https://aws.amazon.com/jp/blogs/security/how-to-set-up-federated-single-sign-on-to-aws-using-google-apps/
  • 設定のためには、Directory APIの設定ができる権限が必要
  • ユーザー情報の一つの属性として設定する必要がある
    • 権限は個人単位で設定しなければならない
  • 結論としては利用しない
    • APIで構築することに寄せるか、consoleでの設定に寄せるかしてほしい
    • 権限管理がグループ・組織ではなく、個人に紐づくのはびみょう

設定手順について

https://aws.amazon.com/jp/blogs/security/how-to-set-up-federated-single-sign-on-to-aws-using-google-apps/

基本的には上の手順でやってけば良い。現在はG Suite側にAWSのコネクタがあるので、それを使えばもっと手間が省ける。 ざっくりいうと、下記のような内容になる。ここでは、細かい作業内容を詳細に記載しない。

  • G Suite側にてIdpの証明書を発行する
  • AWS側にてG SuiteのIdpを登録する
  • AWS側にて G Suiteからフェデレーションアクセスする際のロールを作る
  • G Suite側にてフェデレーションアクセスする際のロールを指定する custom schemaを作成する
  • G Suite側にてService Providerの設定を行う

SPにアクセスする方法

f:id:selmertsx:20180613150820p:plain

これはめっちゃ便利。みんなchrome使ってるだろうし、IDaaS専用のページから遷移しなくてよいの良い。

微妙だったところ

設定にAPIが必要である

f:id:selmertsx:20180613150837p:plain ※ 面倒だったので、webコンソール上から実行。

{
  "customSchemas": {
    "SSO": {
      "role": [
        {
          "value": "arn:aws:iam::xxx:role/TestGsuiteReadOnlyRole,arn:aws:iam::xxx:saml-provider/TestGoogleApps",
          "customType": "TestGsuiteReadOnlyRole"
        }
      ]
    }
  }
}
{
  "fields":
    [
      {
          "fieldName": "role",
          "fieldType": "STRING",
          "readAccessType": "ADMINS_AND_SELF",
          "multiValued": true
      }
  ],
  "schemaName": "SSO"
}
  • SPとの連携において、SP側が求めるパラメータを渡すためには、APIを使って G Suite側のユーザー情報を拡張しなければならない。
  • 影響範囲を考えるとstaging環境での検証が必須
  • 事故が起きたときの被害が大きい

AWSのIDaaSとしての権限管理はユーザー単位でしかできない

f:id:selmertsx:20180613151123p:plain

{
  "customSchemas": {
    "SSO": {
      "role": [
        {
          "value": "arn:aws:iam::xxx:role/TestGsuiteReadOnlyRole,arn:aws:iam::xxx:saml-provider/TestGoogleApps",
          "customType": "TestGsuiteReadOnlyRole"
        }
      ]
    }
  }
}

こんな感じでAWSとの連携においては、ロールの設定はユーザー個人ごとに行う必要がある。人の異動に応じて権限を変えたいので、個人ではなくてグループ・所属に権限をもたせたい。それをやるためには、権限管理用のサービス作らないといけない。

Rubyで型チェックをしてくれるsteepを試してみた

TL;DR

使い方

class Person
  # @dynamic name, contacts
  attr_reader :name, :contacts

  def initialize(name:)
    @name = name
    @contacts = []
  end

  def guess_country()
    contacts.map do |contact|
      case contact
      when Phone
        contact.country
      end
    end.compact.first
  end
end

class Phone
  # @dynamic country, number
  attr_reader :country, :number

  def initialize(country:, number:)
    @country = country
    @number = number
  end

  def ==(other)
    if other.is_a?(Phone)
      other.country == country && other.number == number
    end
  end

  def hash
    self.class.hash ^ country.hash ^ number.hash
  end
end
# 型定義ファイル
class Person
  @name: String
  @contacts: Array<Phone>
  def initialize: (name: String) -> any
  def name: -> String
  def contacts: -> Array<Phone>
  def guess_country: -> (String | nil)
end

class Phone
  @country: String
  @number: String
  def initialize: (country: String, number: String) -> any
  def country: -> String
  def number: -> String
end
bundle exec steep check -I test.rbi test.rb
# 何も問題がなければ何も出ない

問題がある場合

phone = Phone.new(country: 'Japan', number: 1)

こんな感じでstring型が期待される場所に、integerを渡してみた.

➜  steep git:(master) ✗ bundle exec steep check -I test.rbi test.rb
test.rb:40:44: ArgumentTypeMismatch: receiver=::Phone.class constructor, expected=::String, actual=::Integer (1)

という訳で、string型が求められてる場所にintegerを渡すと怒ってくれる。

忘れがちな設定

@dynamic コメント

class Person
  # @dynamic name, contacts
  attr_reader :name, :contacts

attr_readerなど、methodとして定義しないattributesを扱う場合、 # @dynamic attribute_name で指定しなければならない。 これを忘れると、下記のようにmethod missingのエラーが出る。

➜  steep git:(master) ✗ bundle exec steep check -I test.rbi test.rb
test.rb:1:0: MethodDefinitionMissing: module=::Person, method=name (class Person)
test.rb:1:0: MethodDefinitionMissing: module=::Person, method=contacts (class Person)

所感

  • 普通に自分の個人プロダクトでは採用して良いのではなかろうかというレベルに来てる。
  • Railsくらいの規模のコードで問題なく使えるか検証したい
  • 大きなライブラリも導入してくれると、コードを読むときに助かる気持ち

【雑メモ】AWS CloudTrailメモ

  1. CloudTrailとは

AWSのユーザー、ロールによって実行されたアクションを記録するもの。 AWSマネジメントコンソール、AWS CLIAWS SDKで実行された全てのアクションが記録される。 ログはS3に記録され、SSEを使用して暗号化される。 記録するデータには大きく分けて、管理イベントとデータイベントがある。 データイベントについては、記録するか否か選ぶことができる。

アクセスから15分後に、ログファイルは配信される。これは変更することも可能である。ログファイルは監査に使われるため、通常の権限では変更できないものでなければならない。ログファイルは、splunk等の外部サービスとも連携することが可能である。CloudTrailでは全てのAWSサービスが、対応している訳ではない。

管理イベント

  • セキュリティグループの設定 (例: IAM AttachRolePolicy API オペレーション).
  • デバイスの登録 (例: Amazon EC2 CreateDefaultVpc API オペレーション).
  • データをルーティングするルールの設定 (例: Amazon EC2 CreateSubnet API オペレーション).
  • ログ記録の設定 (例: AWS CloudTrail CreateTrail API オペレーション).

データイベント

  • Amazon S3 オブジェクトレベルの API アクティビティ (例: GetObject、DeleteObject、PutObject API オペレーション).
  • AWS Lambda 関数の実行アクティビティ (Invoke API).

CloudWatch Logsとの連携

ログデータのモニタリングに使えるやつ。CloudTrailで取得したデータを、ここに突っ込むのもあり。ルールを指定して、それに合致するLogが来たらアラート飛ばすなども可能。

CloudTrailとリージョン

基本的に、AWSパーティション内の全てのリージョンで監査する。 (ap-northeast01aとかそういうことかな?) あまり使用しないリージョンでリソースされたときなどの、異常なアクティビティを検知できるから。1つのリージョンに対してMAX5つのtrail logを残すことが可能。

グローバルサービスイベントについて

AWS Identity and Access Management (IAM)、AWS STSAmazon CloudFront、Route 53などの、複数のリージョンにまたがって存在するリソースのイベント。これらは、US East (N. Virginia) に記録される。設定を失敗すると、重複して送られてしまうこともあるのでよく確認すること。

https://docs.aws.amazon.com/ja_jp/awscloudtrail/latest/userguide/cloudtrail-create-and-update-a-trail-by-using-the-aws-cli.html#cloudtrail-create-and-update-a-trail-by-using-the-aws-cli-examples-gses

CloudTrailで取得できるログについて

AccountID_CloudTrail_RegionName_YYYYMMDDTHHmmZ_UniqueString.FileNameFormat
111122223333_CloudTrail_us-east-2_20150801T0210Z_Mu0KsOhtH1ar15ZZ.json.gz
  • UniqueStringは自動で生成されそうな気配 (未確認)

実際のログについて

{"Records": [{
    "eventVersion": "1.0",
    "userIdentity": {
        "type": "IAMUser",
        "principalId": "EX_PRINCIPAL_ID",
        "arn": "arn:aws:iam::123456789012:user/Alice",
        "accessKeyId": "EXAMPLE_KEY_ID",
        "accountId": "123456789012",
        "userName": "Alice"
    },
    "eventTime": "2014-03-06T21:22:54Z",
    "eventSource": "ec2.amazonaws.com",
    "eventName": "StartInstances",
    "awsRegion": "us-east-2",
    "sourceIPAddress": "205.251.233.176",
    "userAgent": "ec2-api-tools 1.6.12.2",
    "requestParameters": {"instancesSet": {"items": [{"instanceId": "i-ebeaf9e2"}]}},
    "responseElements": {"instancesSet": {"items": [{
        "instanceId": "i-ebeaf9e2",
        "currentState": {
            "code": 0,
            "name": "pending"
        },
        "previousState": {
            "code": 80,
            "name": "stopped"
        }
    }]}}
}]}

その他メモ

https://digitalforensic.jp/2011/11/10/column182/

AWS Fintech リファレンスガイドを読んで色々調べた

概要

AWSにおけるセキュリティ対策周りがまとまった資料がある。

FINTECH - アマゾン ウェブ サービス (AWS)

この資料を読んで、いまいち分からなかったサービスの概要を調べてまとめてみた。

AWS FinTechガイドラインで出てきた機能一覧

AWS Organization

複数のAWSアカウントを統合管理するためのサービス。 https://docs.aws.amazon.com/ja_jp/organizations/latest/userguide/orgs_introduction.html

だれが、どんな権限で、どのアカウントにアクセス可能なのかを全てのメンバーアカウントに対して制御できる。組織の中で行われたアクティビティを監視できる。

CloudTrailとの連携

AWSに対するAPIコールを記録する。誰が、どのIPで、いつ、何のAPIを実行したのか取得できる。これらのログはS3に保存される。ログファイルはSSEで暗号化されている。フェデレーションで作成されたユーザーでも、これらのデータは取得できる。

https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-user-identity.html#STS-API-SAML-WIF

CloudWatch Eventsとの連携。

Organization内の各アカウントにおける、AWSリソースへの設定変更を記録できるようになる。変更されたらログを残したり、slackに通知したりするなど。

Amazon CloudWatch Logs

およびその他のソースのログファイルの監視、保存、アクセスができるようになる。アプリケーションログをモニタリングし、規定回数委譲エラーが出たら、管理者に通知するなど。サーバーにインストールする必要はありそう。ログ監視ツールっぽい。何を、何で監視するかは一定考えたほうが良さそう。あと、そのツールがCloudWatch Logsなのか。こういう事例もあるっぽい。

http://techlife.cookpad.com/entry/2018/05/02/161231

Amazon GuardDuty

AWS CloudTrailイベントログ、DNSログ、VPSログを計測する。 それによってマルウェアやマイニングビットコインに使われている侵害されたEC2インスタンスの検出。また、AWS アカウントのアクセス動作をモニタリングして、未許可のインフラストラクチャのデプロイ(これまで使用されたことのないリージョンへのインスタンスのデプロイなど)や、異常なAPIコール(パスワードポリシーがパスワードの強度が下がるものに変更されるなど)などを検知する。

Amazon Inspector

自動的にアプリケーションを評価し、脆弱性やベストプラクティスからの逸脱がないかどうかを確認する。 https://aws.amazon.com/jp/inspector/

( 具体的にどうやってるのか不明 )

AWS Firewall Manager

AWS Organizationで管理している各種サービスについて、下記のセキュリティ対策を自動で入れ込める。

  • Cross-site scripting
  • Geographic origin
  • SQL injection
  • IP address or range
  • Size constraint
  • String or regular expression

https://aws.amazon.com/jp/blogs/aws/aws-firewall-manager-central-management-for-your-web-application-portfolio/

自プロダクトのRailsコーディング規約

参考図書

みんなでコーディングする上で、共通知識とする書籍を決める。

  • リーダブルコード
  • Object Oriented Programing in Ruby
  • Perfect Ruby
  • Effective Ruby

Service Objectsは使わない

理由としては、チームみんなで理解してやってくには難易度が高すぎるため。 基本的に app/models 下に置く。まずは、Object思考でちゃんとやれないか考えてみる。

https://www.netguru.co/blog/service-objects-in-rails-will-help

一つのテストの中で、expectを書きすぎてはいけない

何をテストしたいのかわかりにくくなるため。

https://github.com/reachlocal/rspec-style-guide#the-one-expectation

ActiveRecord standardのassociations, validations, or scopes はテストしない

これらのテストをすることは、ActiveRecordのmethodをテストしていることと同じであるため

https://signalvnoise.com/posts/3159-testing-like-the-tsa

でも、正規表現とかバリバリ使ってます!って話ならやってもいい。

Railsの controllerにて、before_action等でインスタンス変数のセットをしないこと

DRYに見えるかも知れないけれども、 viewにどのような変数を渡すのか判別が難しくなるので禁止。

Migrationの中でDBへのinsertをしてはいけない

たとえば、User.create(name: "hage") という形でレコードを追加する migrateがある。 その後、user table に対して、age カラムをnullや空文字を許容しない形で追加しようとすると、 migrate:reset 時に必ずエラーが出てしまう。

よって、以降の変更に制限を加えるコードになってしまうため、migrateの中での insertは許容しない

Rails のViewの中で、Databaseへのアクセスはしてはいけない

MVCの概念から考えても良くないし、queryが発行される場所を制御できなくなるため、 パフォーマンスチューニングが必要なときに難しくなるので。

ロガーを使わない程度のメッセージ標準出力には p ではなく puts を使う

MySQLにおけるString型カラムはNULLを許容しない

  • string型にnullを許容すると、nullチェックする必要性が生まれる
  • nullチェックするコストは省きたい
  • nullチェックする必要があるカラムなのか、常に判断するのは難しい
  • よって、原則としてstring型はnullを許容しないようにする

定数はconfig/initializers/constants.rb 周りに置く

railsconfig にデータを保持させると、データを追加させたときに、みんな railsconfig を更新しなければならなくて手間。settings はコードに乗せてはいけない情報のみを扱いたい。

http://guides.rubyonrails.org/autoloading_and_reloading_constants.html#autoloading-and-initializers

ActiveRecord モデルが存在しない _id のカラム命名は都度検討する

_id で終わるカラム名は慣例的な経緯もあり、ActiveRecord モデルの存在を想起する場合がある。 一方で、外部システム連携時など、外部システム側で使用しているフィールド名 xxx_idカラム名としてそのまま採用した方が見通しよく把握できるケースも多い。

ここはチーム内で良く議論する必要がある。

ActiveRecord Callbackは使わない

class User
  after_create :send_mail
end

mailを送られるの気づかなくて爆死などを防ぎたい。 あと、factoryを作るのがツラくなる。そのときは便利に見えても、後々負債になるから禁止。

Queryは基本的にScopeで書く

※ これはプロダクト毎に意見が分かれる部分があると思うので、一概に適用はしないこと。

Rails上で発行されるQueryについて、全てModelに集約されていると、どのモデルでどのようなQueryが発行されているのか見通しがよくなる。同じ様な結果を得るために、似たようで微妙に異なるQueryが大量に生成される問題を避けることにも繋がるため、基本的に発行されるQueryはModelで参照できるScopeとして定義すること。

参考文献

GitHubで自分が作ったPRを追うためにTrailerが便利だった

モチベーション

GitHub上で、自分が管理していないリポジトリにPR投げると、その後どうなってるのか追い忘れることがあった。相手にとっても迷惑かなって思ったので、なんとかしたい。

結果

f:id:selmertsx:20180515203733p:plain 自分が作ったPRが見れる。便利

設定方法

1. Download

https://ptsochantaris.github.io/trailer/

ここからアプリをDL

2. リポジトリの設定

  1. リポジトリをwatchingにする f:id:selmertsx:20180515203704p:plain

  2. 通知するリポジトリの設定をする f:id:selmertsx:20180515203728p:plain

自分が管理しているリポジトリは PRもissueも全て表示するようにして、その他のリポジトリは自分がPR/Issueを作ったもののみ表示するようにした。 ( そのときに、該当リポジトリをwatchingにしておく )