OpenID Connect CIBAフローを実装してみた感想
最近OpenID ConnectのCIBAフローを実装する機会があったので、その感想をメモっときます。
そもそもCIBAフローって何?
authleteの川崎さんのqiitaがマジ詳しいです。
認可コードフローなどの通常のフローはリダイレクトでトークンのやりとりを行うのでブラウザ上で動くことが前提となりますが、CIBAはブラウザ以外での認証リクエストを受け付けることができるので、いろんなとこに応用が効くと思ってます。
以下、川崎さんの記事を読んだりして、CIBAフローの雰囲気を理解した方向けに、実装してみた感想をカンタンにまとめます。
サーバサイドの実装
すでにOpenID Connectの実装があるんなら、POLLモードで最低限のCIBAフローそのものの実装はそこまで難しくないです。
tokenエンドポイント以外は新規実装でよいので既存の設定の整合性を考えなくてよいですし、tokenエンドポイントはgrant_typeで処理を分岐させるだけです。
ただ、認証リクエストがきたときにユーザに通知を行う必要があるのですが、どんな手段で通知するかはCIBAの仕様範囲外です。
普通のOP/IdP実装だとユーザ通知なんて用意してないでしょうから、そこが完全にゼロベースでの実装となって非常にめんどくさかったです。
なお、まだ手元の実装では対応してはないですが、POLLモードができれば、PINGモードもちょちょいと実装追加で対応可能です。
でもPUSHモードはトークンを直接外部のエンドポイントに送りつけることになるので、そのエンドポイントの確認と管理(RPが管理しているエンドポイントじゃなかったら即事故になる)が必要なのがネックになって、正直どう実装したらよいか全くわからないです。
クライアントサイド
サーバ実装とあわせてクライアントも必要になるわけですが、POLLモードでは以下の2タイプのリクエストを投げるだけです。
curlで確認できるくらいカンタンなんですが、カッとなってgolangでラップしてみました。
READMEのサンプルコード見ていただければわかると思いますが、認証処理が 関数一つ呼び出しでOK というくらいカンタンです。
ID danceを理解しないと実装が困難な認可コードフローやimplicitフローとは大違いです。
個人的にはココがCIBAフローの一番嬉しいトコだと思っています。
応用のユースケースで、ネイティブアプリのバックエンドサーバでのユーザ認証を考えても、独自拡張したID danceなんかを実装しなくてすむので、本当に本当にメリットしかカンジられません。
これだけクライアント実装がカンタンなら2020年にはCIBAフローが市民権をえるんじゃないかなー。。。
最後に
CIBA対応のIdPがもっと出てくるといいなぁと思います。
SSRF対応のgolangライブラリをつくった
SSRFそのものの解説は、最近公開されたはせがわようすけさんのスライドが詳しいので、そちらを見ていただくとして、、、
最近では実際の攻撃事例もでてきている、ということで、ついカッとなってgolangのライブラリを作ってみました。
使い方は antissrf.Client()
で *http.Client
が帰ってくるのでそれをフツーに使うだけというカンタン仕様です。
プライベートアドレスとかループバックアドレスとかリンクローカルアドレスにアクセスしようとするとerrorが帰ってきます。
var client = antissrf.Client() func main() { // errが帰る _, err := client.Get("http://169.254.169.254/") }
antissrf.Client()
は *net.IPNet
を可変長引数で受け付けるので、他にブラックリストを追加したいケースにも対応可能です。
var client = antissrf.Client( antissrf.MustParseCIDR("192.0.2.0/24"), antissrf.MustParseCIDR("198.51.100.0/24"), antissrf.MustParseCIDR("203.0.113.0/24"))
名前解決のあとのtcpコネクションをはる直前のremoteのIPアドレスをチェックしているので、DNS Rebindingな攻撃にも安心です。
そんなわけで、ご活用ください。
Security Engineering Casual Talksで喋ってきた
本日Security Engineering Casual Talks #1というイベントがあったんですが、そこでKMSの利用について喋ってきました。
内容的にはenv-injectorの利用につながるアレコレだったりするんで、個人的には目新しい内容というわけではなかったんですが、これで世の中からDBパスワードとか他サービスのアクセストークンとかがそのままgithubのリポジトリに保存されるケースが少しでも減ればよいなぁ、と思っています。
なお、会場では全く関係ない某社のサービスのTシャツを着ていたんですが、誰からツッコまれませんでした。カナシミー
(追記)
- 某所からプレゼンをiframeで貼ればいいのに、、、という電波を受け取ったので対応
- 某所から「"復号化"ではなく"復号"だろ、ボケ」というツッコミがきちゃいました。そのとおりだと思います。ゴメンナサイ。プレゼン資料を修正するのは面倒なので、こちらでお詫びさせてください。
(追記その2)
- 復号化警察の方から追加で「SystemManagerじゃなくてSystemsManagerだ」というツッコミも来たので、もろもろまとめて資料の方、修正させていただきました。
......なれない発表してゴメンナサイ。もう許して下さい。id:kakku22 兄さん。(´;ω;`)ブワッ
golangの名前解決について
こちらのgolangのstatic link化に関する2年前の記事なのですが、先月こんなコメントをいただきました。
golangで書いたアプリケーションのstatic link化 - okzkメモb.hatena.ne.jpそのtagを指定するとどう動く、ってのがイマイチわかりにくい。netパッケージだとnetgoまたはnetcgoを指定することで名前解決の方法が変わったりするし。
2018/03/27 18:36
そんなわけでビルド時にtagでnetgo指定したら、何が起こるかというハナシです。
あまり知られてないと思いますが、golangのDNSの名前解決の方法が以下の2種類があります。
- libcの
getaddrinfo
を使う - pure goの実装
前者のlibcの getaddrinfo
を使う場合はdynamic linkになってしまいますが、
CGO_ENABLED=0
を指定した場合や、netgoを指定したらpure go版の実装に切り替わります。
詳細はソースを検索してみてください。
https://github.com/golang/go/search?l=Go&q=netgo&type=Code
cgo
や netgo
のビルドタグをみると、どのソースがビルドされたりされなかったりがわかるのでオモシロイと思います。
私が把握しているnetgo指定をした場合の挙動の違いは以上なのですが、他にもあったら教えていただけるとウレシイです。
こちらからは以上です。
env-injectorをSecrets Managerに対応させました。
AWS Secrets Managerリリースされましたね。
そんなわけでenv-injectorをSecrets Managerに対応させました。
ダウンロードはgithubのリリースページからどうぞ。
使い方は、こんなカンジでSecretが登録されている状態で
$ aws secretsmanager get-secret-value --secret-id prd/db --query SecretString --output text {"DB_USER":"scott","DB_PASSWORD":"tiger"}
ENV_INJECTOR_SECRET_NAME
を指定して任意のコマンド実行すれば、環境変数が設定された状態でそのコマンドが実行できます。
$ export ENV_INJECTOR_SECRET_NAME=prd/db $ env-injector printenv | grep DB_ DB_USER=scott DB_PASSWORD=tiger
必要なIAMのポリシーはこんな感じです。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue" ], "Resource": [ "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:prd/db-*" ] } ] }
もちろん今まで通り、ENV_INJECTOR_PATH
やENV_INJECTOR_PREFIX
での指定も引き続き有効です。
まあココまでだとシンプルなんですけど、Secrets ManagerはRDS等対象コンポーネント毎にシークレットを管理するものっぽいので env-injectorの「アプリケーションで使うシークレットを管理する」という方向性とちょっとズレがあります。
というわけで、複数のSecretやパラメータストアと組み合わせるような機能も追加しました。
以下のようにSecretやパラメータストアの設定のyamlをパラメータストアに保存しておいて
$ aws ssm get-parameter --name /meta/prd/wap.yaml --query Parameter.Value --output text # secretの名前を指定 - secret_name: prd/db1 # RDSのsecretとかだと同じkeyでクレデンシャルが保存されるので、 # 複数になったときに区別をつけるためのprefix(オプション) env_prefix: db1 # 環境変数にする際にkeyを大文字化したい場合に指定する(オプション) capitalize: true - secret_name: prd/db2 env_prefix: db2 capitalize: true # その他、パラメータストアからも読み出し可能 # (ENV_INJECTOR_PATHで指定するのと同じ機能) - parameter_store_path: /prod/wap
あとは ENV_INJECTOR_META_CONFIG
でyamlのパスを指定して任意のコマンドを実行するだけ。
$ export ENV_INJECTOR_META_CONFIG=/meta/prd/wap.yaml $ env-injector printenv DB1_USERNAME=alice DB1_PASSWORD=hogehoge DB1_HOST=xxxx.ap-northeast-1.rds.amazonaws.com DB1_PORT=3306 (中略) DB2_USERNAME=bob DB2_PASSWORD=mogemoge DB2_HOST=yyyy.ap-northeast-1.rds.amazonaws.com DB2_PORT=3306 (中略) SOME_OTHER_CONFIG_FROM_PARAMETER_STORE=hogeohge
これで既にパラメータストアで管理していたクレデンシャルのうち、一部をSecrets Managerに移行する、とかもできるようになりました。
そんなこんなで、引き続きご利用いただけると、ウレシイですー
……対応しといてアレなのですが、Secrets Managerってパラメータストアと違ってオカネかかるし、機能的にも大きく変わらない(lambdaでローリング更新するのはパラメータストアでもできる)し、ぶっちゃけパラメータストアでええのんちゃうん?という気がしています。(;・∀・)
はてさて、どーなんすかね?
golang 1.9.4とCGO_CFLAGS_ALLOW環境変数
golangで1.9.4のセキュリティアップデートでてます。
ところがアップデートすると、cgoのCFLAG等で制限されたフラグを使ってるライブラリで以下のようなメッセージを吐いてビルドがこけるようになるケースがある模様。(´;ω;`)ブワッ
$ go get github.com/crewjam/go-xmlsec go build github.com/crewjam/go-xmlsec: invalid flag in #cgo CFLAGS: -w
そんなときは、慌てず騒がずトライアンドエラーで許可するフラグの正規表現つくってCGO_CFLAGS_ALLOW
環境変数にいれれば、ビルドはできるようになります。
$ export CGO_CFLAGS_ALLOW="\\A(-w|-UXMLSEC_CRYPTO_DYNAMIC_LOADING)\\z" $ go get github.com/crewjam/go-xmlsec
でもエラーになったからって何も考えずに-fplugin=
とか-plugin=
とかを許可するとセキュリティアップデートのイミがなくなるので、フラグのイミはちゃんと確認することをオススメします。
取り急ぎ、以上。
MackerelでのECSのタスクのメトリクスの2018年版
id:kakku22 兄やんから「Mackerel Meetupで発表することになったぜ」と連絡をもらったのですが、そのハナシの流れで、 「コンテナのメトリクスを取るイケてるやり方をブログにしてちょ」といわれてしまったので PoCレベルで恐縮ですがエントリにまとめます。
前回までのおさらい
ECSホストにMackerelエージェントいれて、そのホストで動いているメトリクスを収集するというカンジです。
とりあえずはまあ動くとはいえ、なんとも微妙な点としては以下の通り
- シェル芸が必要
- ダッシュボード作りこみが必要
- ECSホストでMackerelのagentインスコする必要がある(Fargateの場合どーすんの)
今回のやり方
図にするとこんなカンジ
エージェントについて
PoCということでドキュメントもクソもない状況ですけど、ソースはこっち。 github.com
んでラップしたイメージも用意しました。
https://hub.docker.com/r/okzk/mackerel-metrics-collect-agent/
memcachedのメトリクス収集を例にすると、まずはこんなカンジのconfをS3に上げておきます。
[plugin.metrics.memcached] command = "mackerel-plugin-memcached -host memcached"
confはmackerel-agent.conf互換なので、違和感は少ないと思います。
次にagentのコンテナをmemcachedのサイドカーコンテナとして、memcachedにリンクさせて
環境変数CONF_S3_URI
にs3://YOUR-BUCKET/path/to/memcache-metrics.conf
を指定して動かします。
タスクロールでs3:GetObject
できるように許可しておいてください。
この時ネットワークモードがbridgeだったらホスト側のポートを0にしてダイナミックポートで動くようにしておきます。
(ネットワークモードがawsvpcやhostだったら、そのまま2018を指定しておきます)
イメージには標準プラグインを同梱していますが、さらに追加したい場合はmkrでインストールできるようにしているので
MKR_INSTALL_PACKAGE
環境変数でインストールしたいプラグインを指定してください(スペース区切りで複数可)。
プラグインについて
ソースはこっち。PoCなんでドキュメントも(ry
こんなカンジで、クラスタ名/サービス名/agentのコンテナ名/ネットワークモードを指定してプラグインの設定をして動かせば、イイカンジにタスクのメトリクスを収集してきます。
[plugin.metrics.memcached] command = "mackerel-plugin-ecs-task-metrics -cluster my-cluster -service dev-okzk -containerName mackerel-metrics-collect-agent -networkMode bridge"
プラグインを動かすホストについて
ここまででプラグインを動かすホストのカスタムメトリクスとしてECSタスクのメトリクスが収集されるようになったんですが、 通常のホストでプラグイン設定をすると、そのホストがふっとんだときに微妙なカンジになっちゃいます。
ということで、その「プラグインを動かすホスト」もECSで動かすことができるようイメージを用意しました。
https://hub.docker.com/r/okzk/mackerel-ecs-task-metrics-collector/
このイメージでは/var/lib/mackerel-agent/id
ファイルをS3に退避しておいて使いまわすようにしているので、仮想的に一つのホストとして
メトリクスを継続して収集できます。
使い方はapikeyとかも含めた完全なconfをS3にアップロードしておきます。
apikey = "mackerelのAPIキーだよ" cloud_platform = "none" display_name = "ECS Task Metrics" [filesystems] ignore = ".*" [plugin.metrics.memcached] command = "mackerel-plugin-ecs-task-metrics -cluster my-cluster -service dev-okzk -containerName mackerel-metrics-collect-agent -networkMode bridge"
IAMロールは以下のモノが必要です。
- conf等のアクセスのために
s3:GetObject
,s3:PutObject
- agentアクセスのために
ec2:DescribeInstances
,ecs:ListTasks
,ecs:DescribeContainerInstances
,ecs:DescribeTasks
次にtask definitionの環境変数で以下のように指定。
最後にサービス設定で最大1台をキープするように実行します。
そーすると、ホストメトリクスとしてイーカンジにメトリクス収集できます。
やったね。
まとめ
S3にconfぶっこんどいて、ポチポチタスク設定すれば、比較的カンタンにイーカンジにメトリクス収集できるようになりました。
awsvpcにも対応しているのでFargateも怖くないよ。
追記(2018-02-05)
ホストのカスタムメトリクスではなく、サービスメトリクスにしてしまうのがいい気もしてきた。
……っと思ったけど、グラフ定義とか#
のワイルドカードの扱いとかアレコレ考えると、サービスメトリクスだとやりにくいか。。。
やっぱり、ホストのカスタムメトリクスの方が都合がいいかも(ぉ
追記(2018-02-05 20時ごろ)
Mackerelの公式でコンテナサポートするそうです。そんなわけで本エントリの内容はPoCのママ、Deprecatedになりそうっすね。(´;ω;`)ブワッ