selmertsxの素振り日記

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

CloudSQL をRailsで使ってみる

モチベーション

  • 今、個人で作ってるプロダクトを試しに GKEで動かしてみたい
  • GCPだと、RDBはCloudSQLを利用した方が良さそう
  • GKEの前に、CloudSQLを触ってみる

今日の学び

  • CloudSQLは、Globalからアクセス可能であるが、IPのホワイトリストかProxyを利用する必要がある
  • 開発時には固定IPを取得することが難しいので、Proxyを利用するのが良い

概要

下記2つのドキュメントを見て作業した

インスタンス作成

gcloud sql instances create pomodoro-dev-db --tier=db-f1-micro --region=asia-northeast1
gcloud sql users set-password root % --instance pomodoro-dev-db --password [PASSWORD]

接続名の取得

shuhei_morioka@cloudshell:~ (speee-dev-optim)$ gcloud sql instances describe pomodoro-dev-db | grep connectionName
connectionName: xxx:asia-northeast1:pomodoro-dev-db

このconnectionNameを Proxyにも利用する

Cloud SQL Proxyの利用

curl -o cloud_sql_proxy https://dl.google.com/cloudsql/cloud_sql_proxy.darwin.amd64
chmod +x cloud_sql_proxy
bin/cloud_sql_proxy -instances=xxx:asia-northeast1:pomodoro-dev-db=tcp:3307
mysql -u root --host 127.0.0.1 --port 3307 -p

https://cloud.google.com/sql/images/proxyconnection.svg

今日覚えたコマンド

gcloud sql instances describe pomodoro-dev-db
backendType: SECOND_GEN
connectionName: xxx:asia-northeast1:pomodoro-dev-db
databaseVersion: MYSQL_5_7
etag: '"xxxx/Mw"'
gceZone: asia-northeast1-a
instanceType: CLOUD_SQL_INSTANCE
ipAddresses:

次回の対応

GKE動かしてき Connecting from Kubernetes Engine  |  Cloud SQL for MySQL  |  Google Cloud

GCPエンタープライズ設計ガイドの読書メモ

はじめに

業務でGCPの一部を利用することになったため、概要を把握するためにこれをサラッと読んだ。 この本はGCPの様々な機能に関する概要をまとめたもので、これを読むだけでGCPがガンガン使えるようになるものではない。 あくまでGCP全体の概要を把握するために読むものである。 本の中では、AWSの機能のこれにあたるという説明が多くされているため、AWS経験者なら頭に入りやすいのではないだろうか。

この記事では、僕が必要としていた部分だけ抽出して文書にしている。

AWSGCPの違い

  • AWSはCloudのサービスをリードしている
  • GCPはCloudのテクノロジーをリードしている
  • AWSは多機能
  • GCPはシンプル
  • AWSは従来のITインフラをそのまま抽象化した
    • VPCなどなど
  • Googleはソフトウェアエンジニアが基盤を意識しなくても作れるようになっている
    • ITインフラの内部が隠蔽されている
  • AWSは休日も含む24時間365日の日本語サポートがある
  • GCPの日本語サポートは、平日9~17時

Computing Service

  • GCPサービス => AWSで該当するもの
  • Google Compute Engine => AWS EC2
  • Google App Engine => AWS Elastic Beanstalk
    • スケールアウト時にはコンテナのプロセスを起動する
    • Standard EnvironmentとFlexible Environmentの2つのタイプが存在する
    • SE版は機能が制限されているが、めちゃくちゃ早い
    • FE版はDockerで動くので自由度が高いが、まぁまぁ遅い
    • 新しいバージョンの環境へのアクセスについては、柔軟に振り分けられる
    • ABテストなどにも使える
    • cronで定期実行も可能
  • Google K8s Engine => AWS EKS
  • Google Cloud Functions => AWS Lambda

Cloud SQL

  • Cloud SQLは、ストレージの自動拡張、Failover Replica機能、Read Replicaの作成の機能を持ち、耐障害性・スケール性能に優れている。
  • Cloud SQLにアクセスする場合は、Cloud SQL Proxyを利用して、IAMを利用したプロジェクトレベル・インスタンスレベルでのアクセス制御を行うこと。
  • Cloud SQLでは転送中・保存済みのデータはすべて暗号化されている。

ID管理

  • GCPではClod IAMを利用して、リソースの権限制御をしている
  • Cloud IAMは下記のIDに対して設定が可能である
    • Googleアカウント
    • サービスアカウント
      • 利用するアプリケーションに紐づくアカウント
    • Googleグループ
    • G Suiteドメイン
    • Cloud Identityドメイン
      • GmailやドライブなどのG Suiteのサービスを必要としないユーザー向けのドメイン

Cloud IAMでのRole

<service>.<resource>.<verb> といった形式で定義する。

  • Primitive roles: プロジェクトレベルで設定する Owner, Editer, Viewerの権限
  • Predefined Roles: GCPのリソース単位で設定できる役割。App Engine管理者はApp Engineのすべての設定が可能
  • Custom roles: 個別に作成するrole. メンバーやサービスアカウント毎に付与可能.
  • Cloud IAMのアクセス制御ポリシーは、Organization/Folder/Project/Resourceという階層になっている
  • 上の階層で定められた権限が、下位の階層に継承される

KubernetesとGKEの学習

最初に

この記事は、下記の書籍を読んだ上で自分の理解を整理するためにまとめたものです。

マルチホストでDockerを動かそう

GCPでのk8s活用について

  • GKEを利用する上で必要なサービスは下記の通り

Google Container Build

  • Docker Imageを作成するためのコマンドツール
  • Imageを作るためのソースリポジトリは色々選べる

Google Kubernetes Engine

  • Dockerコンテナを管理するフルマネージドサービス
  • k8sを利用しているので、kubectlコマンドなどで操作可能

Google Container Registry

Kubernetesの概要

アプリケーションの構成管理

Pod

  • 複数のコンテナをまとめたもの
  • アプリケーションデプロイの単位
  • 適切にスケールできるように、役割の異なるものは別ポッドにすること
  • Podは必ず同じノード上にデプロイされる

ReplicaSet

  • Kubernetesクラスタ上であらかじめ指定された数のPodを作成/起動しておくこと
  • 何か問題が起きても、ここで指定した個数分Podが立ち上がるように動き続ける

Deployment

  • ReplicaSetの履歴を管理するもの
  • 定義されたレプリカの数を維持する役目を負うのがReplicaSet
  • ReplicaSetの作成や更新を定義するのがDeployment

Service

  • K8sのネットワークを管理する機能
  • Podに対して、外部からアクセスするときに必要。
  • Load Balancerは、Serviceに対応するIPアドレス+ポート番号にアクセスすると、複数のPodに対するレイヤー4レベルの負荷分散をする
    • トランスポートレイヤー
    • Borgとかと同じ話かな
  • Cluster IP => クラスタ内のPod同士で通信するためのプライベートIPアドレス
  • External IP => 外部のクライアントから接続するためのパブリックIPアドレス

K8sの構成要素

マスター

データストア etcd

クラスタ構成を保持するKVS

Node

  • 実際にDockerコンテナを動作させるサーバ
  • kubelet というエージェントが動いてる
  • kubeletは、Podの定義ファイルに従ってDockerコンテナを実行したり、ストレージをマウントしたりする
  • ノードのステータスを監視して、APIサーバーに通知する機能も持つ

manifestファイル

  • k8sでは、クラスタの構成情報をYAMLまたはJSONで記述する
  • この定義ファイルをmanifestファイルと呼ぶ

docker-compose.yml とか task definitionを思い出すなぁ。

Local環境のアプリケーションに対してAzure ADでOpenID Connectの認証をしてみる (その2)

TL;DR

  • Azure ADでOpenID Connectの認証ができるか確認をした
  • 確認環境は森岡がテスト用に作ったアプリケーション
  • 上記環境はLocal環境で動いていて、Dockerで構成されている
  • lua-resty-openidcを使ってnginxレイヤーだけで認証ができた
  • まだ本番への反映をしていないので、完成度は30%くらい
    • sessionが切れたときに、/login に強制的にredirectさせる方法
    • dockerでない環境での反映手順も考えられていない
  • Azure AD側にアプリケーションを登録しなければ使えないので、ある程度セキュアではなかろうか

Azure ADの設定

Azure AD側の設定は、基本的にAzure AD公式ドキュメントの方法に従う。

  • Azure Potalにサインインする
  • Azure Active Directoryからアプリの登録を行う
  • サインオンURLの設定をする
    • 今回はlocalで確認するので http://127.0.0.1:8080 とした
  • アプリケーションのプロパティから、アプリケーションIDを確認し控えておく
  • 「キー」から鍵を作成して控えておく
  • このアプリケーションにアクセス可能な人をAzure AD上で設定しておく

f:id:selmertsx:20180710140021p:plain

アプリの登録

f:id:selmertsx:20180710140036p:plain

鍵の登録

実装

Docker Build

今回、サクッと確認するためにDockerで環境を構築。 nginxのlua pluginでOIDCができるやつがいたので、こいつを利用させて貰う。

FROM openresty/openresty:1.13.6.2-0-centos
ADD conf.d/app.conf /etc/nginx/conf.d/app.conf
ADD nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
ADD src /usr/local/openresty/nginx/src
RUN opm install zmartzone/lua-resty-openidc
CMD ["/usr/bin/openresty", "-g", "daemon off;"]

openrestyのdocker imageには、opm(openresty package manager)がデフォルトで入っているので、そのopmを使ってlua-resty-openidcをinstallすることができる。

証明書の設定

luasslを利用する際は、lua_ssl_trusted_certificate ディレクティブに証明書の設定をする必要がある。この証明書だけれども、実はopenrestyのdocker imageの中にデフォルトで入っているので、今回はそれを利用する。

➜  pomodoro git:(master) ✗ docker exec -it pomodoro-web /bin/bash
[root@bba9dae7baca /]# ls -al /etc/ssl/certs/
total 8
drwxr-xr-x 2 root root 4096 Apr  2 18:38 .
drwxr-xr-x 5 root root 4096 Apr  2 18:38 ..
lrwxrwxrwx 1 root root   49 Apr  2 18:38 ca-bundle.crt -> /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
lrwxrwxrwx 1 root root   55 Apr  2 18:38 ca-bundle.trust.crt -> /etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt

これをnginxの設定に記載して終了。実際のコードはここ を参照

Luaコードの設定

-- https://github.com/selmertsx/pomodoro/blob/master/docker/nginx/src/test.lua
local opts = {
  redirect_uri_path = "/",
  discovery = os.getenv("DISCOVERY"),
  client_id = os.getenv("CLIENT_ID"),
  client_secret = os.getenv("CLIENT_SECRET"),
}

local res, err = require("resty.openidc").authenticate(opts)

if err then
  ngx.status = 500
  ngx.say(err)
  ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end

ngx.req.set_header("X-USER", res.id_token.sub)

環境変数の渡し方

discovery, client_id, client_secretはシークレットな情報なので環境変数で渡す。 nginxでは環境変数luaに渡すためには、env directiveを利用する必要がある。

The NGINX environment variable is used internally by nginx and should not be set directly by the user.

公式ドキュメントには上記のように記載されているので、nginx.confの一番上でenv directiveを利用する。

worker_processes  1;
env DISCOVERY;
env CLIENT_ID;
env CLIENT_SECRET;
events {
  worker_connections  1024;
}

なお CLIENT_IDやCLIENT_SECRETはAzure AD設定時に取得したアプリケーションIDや鍵を設定すれば良い。

環境変数DISCOVERYの設定

discovery urlの取得方法については、公式ドキュメントに記載されている通りであるが、少しばかりハマりポイントがあるので解説。ここだけはちゃんと読んでほしい。

  • https://login.microsoftonline.com/{tanent_name}/.well-known/openid-configuration でアクセスし、tanent_idを取得
  • https://login.microsoftonline.com/{tanent_id}/.well-known/openid-configuration の文字列をnginxに設定する

上記設定の理由

ここは興味があれば見るくらいで良い。

まず前提として、Azure ADにおいて、Discoveryは下記2つのフォーマットで取得することができる

  1. https://login.microsoftonline.com/{tanent_name}/.well-known/openid-configuration
  2. https://login.microsoftonline.com/{tanent_id}/.well-known/openid-configuration

lua-resty-openidcを利用する場合、2番のフォーマットでDiscoveryを指定しなければならない。その理由は、lua-resty-openidcの実装上の問題である。上記ライブラリにおいて、openidcの認証は下記のような実装になっている。

-- https://github.com/zmartzone/lua-resty-openidc/blob/master/lib/resty/openidc.lua#L469-L509
      ngx.log(ngx.DEBUG, "response data: "..res.body)
      json, err = openidc_parse_json_response(res)
      if json then
        if string.sub(url, 1, string.len(json['issuer'])) == json['issuer'] then
          openidc_cache_set("discovery", url, cjson.encode(json), exptime or 24 * 60 * 60)
        else
          err = "issuer field in Discovery data does not match URL"
          ngx.log(ngx.ERR, err)
          json = nil
        end
      end

DiscoveryのURLと、discovery urlから返されるissuerのURLの一部が一致しているか見ている。Discoveryから得られるissuerのURLは、https://sts.windows.net/{tanent_id}/ という値になる。そのため、tanent_nameでURLを指定すると一致しないという問題が起きる。よって、Discovery取得用のURLは、tanent_idで指定する必要がある。

認証結果

http://127.0.0.1:8080/test にアクセスすると下記のページにredirectされる

f:id:selmertsx:20180710135840p:plain

認証が終わるとこんな感じ

f:id:selmertsx:20180710135916p:plain

lua-nginx-moduleをdockerで動かす

TL;DR

  • imageの指定方法は、下記のフォーマット
    • openresty/openresty:<openresty-version>-<image-version>-<flavor>
    • flavorとはosのことでcentos, alpine などが入る
    • 今回はこんな感じになった openresty/openresty:1.13.6.2-0-centos
  • 上記方法でイメージを作れば、すぐにluaを実行可能

github.com

設定方法

nginxでluaを実行するために必要な部分のみ抜粋。全体は下記のurlから探してほしい。

https://github.com/selmertsx/pomodoro/blob/29872473c7cbc6c6cbf1fe27bc8d9a9434ae80ba/docker-compose.yml#L43-L52

docker-compose.yml

volumesで自分の手元のnginx設定をcontainerにマウントした。

version: '3'
services:
  nginx:
    build: ./docker/nginx
    container_name: pomodoro-web
    image: pomodoro-web
    volumes:
      - ./docker/nginx/conf.d:/etc/nginx/conf.d
    ports:
      - 8080:80

Dockerfile

手元のnginx設定をaddしている。 この設定ファイルはdocker-composeでmountしているから、開発環境ではいらないかも。

FROM openresty/openresty:1.13.6.2-0-centos
ADD conf.d/app.conf /etc/nginx/conf.d/app.conf
CMD ["/usr/bin/openresty", "-g", "daemon off;"]

nginx app.conf

普通に hello world

server {
  listen 80;
  server_name localhost; 
  keepalive_timeout 5;
  location /hellolua {
    content_by_lua '
        ngx.header["Content-Type"] = "text/plain";
        ngx.say("hello world");
    ';
  }
}

実行確認

➜  pomodoro git:(master) ✗ curl http://127.0.0.1:8080/hellolua
hello world

ということで、問題なく動作していることを確認したぞ!!

Local環境のアプリケーションに対してAzure ADでOpenID Connectの認証をしてみる (その1)

Motivation

Azure ADをIDaaSとして、社内サービスと連携したい。 弊社の社内サービスはPHPJavaRubyと多種多様な言語で書かれており、 またインフラもGCP,AWSと利用されているので、 それらすべてで動くようにnginxレイヤーでIDaaS連携をするようにする必要がある。 ということで、今回はnginxでIDaaS連携ができるライブラリを調査することにする。

AWS のみならば、ALBレイヤーでAzure ADと連携することが可能である。 社内サービスがAWSのみであれば、これを利用するのが最も楽だと思う。

Application Load Balancer 組み込み認証によりログインを簡略化 | Amazon Web Services ブログ

OpenID ConnectとSAML

Azure ADにおいてフェデレーションアクセスをする際のプロトコルとして、OpenID ConnectとSAMLが存在する。 SAMLはIdP側だけでなくSP側もメタデータの登録などが必要であり、設定コストが非常に高い。 そのためOpenID Connectで、求める水準のID管理が出来るのであれば、それにこしたことはない。 なので、一旦OpenID Connectでどこまでできるのか検証を進めていく。

用語の確認

OpenID Connect

  • RP (Relying Party) : SSO対象のアプリケーション
  • OP (OpenID Provider ) : IDの認証を行う機能を有するサーバー

その他、OpenID Connectに関する基礎的な知識は、下記URLを参照。

OpenID Connectまとめ - selmertsxの素振り日記

SAML (Security Assertion Markup Language)

  • SP (Service Provider) : SSO対象のアプリケーション. OpenID Connectで言うRP
  • IdP (Identity Provider) : IDの認証を行うサーバー. OpenID Connetで言うOP

nginx library

github.com

OpenID ConnectのRPに使うための nginx lua library. OpenID Connect DiscoveryとAuthorization code flowを使って、OPとユーザー認証を行う。 セッションはブラウザのcookieか、redis, memcacheで持つ。

使い方

local opts = {
  redirect_uri_path ="",
  discovery = ""
  -- ... その他args
}

local res, err = require("resty.openidc").authenticate(opts)

if err then
  ngx.status = 500
  ngx.say(err)
  ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end

こんな感じ。opsに必要なパラメータを入れた後、require("resty.openidc").authenticate(opts) で認証を実行する。 どんなパラメータが必要なのかはAzure ADと連携するときにでも諸々確認していく

現状の懸念点

  • OpenID Connectで認証した場合、IDaaSでは認証はできるが認可はできないのではないだろうか

www.slideshare.net

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側が完全に把握できる。 設定コストが高いという点ではデメリットでもある。

僕の読んだ資料