selmertsxの素振り日記

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

「成長する企業はなぜSSOを導入するのか」を読んだ

所感

SSOについて、必要性と実現方法の概要を説明している本です。SSOって何?って人がいたら、エンジニア・ディレクター問わず読んでおくと良さそうです。

Single Sign-Onとは

各種サービスへのアクセスを一つにとりまとめるもの。

before 社内で利用される各種サービスへのID/Passを覚えて無ければならなかった

after 全てのサービスへのアクセスは、SSOツールへのアクセスで一元化される。

SSOの実現方法

  • リバースプロキシ方式
  • エージェント方式
  • フェデレーション
  • クライアントエージェント方式

リバースプロキシ方式

ユーザーと業務システムの間に、リバースプロキシサーバーと呼ばれるサーバーを配置するもの。ユーザーからの認証リクエストを、認証サーバーに問い合わせる。ユーザーを代行して業務システムにログインする機能を持つSSOを使えば、業務システム側の認証機能を改修する必要もない。リバースプロキシサーバーを建てる際は、リクエスト数が増大するとそれがボトルネックになりやすい。

エージェント方式

業務システムにエージェントモジュールを組み込む。認証する際には、業務システムから認証サーバーに問い合わせをする。導入する場合、業務システムのプログラムを改修する必要がある。リバースプロキシが使えない場合に採用される。

クライアントエージェント方式

各ユーザーの端末にエージェントモジュールをインストールする方式。各業務システムをエージェントが認識し、ユーザーIDやパスワードをエージェントが入力する。

メリット: 業務システムへの改修が不要 デメリット: ユーザーのOS/利用ブラウザによっては適切に認証できないことがある

フェデレーション

認証基盤がクラウドだったり、オンプレミスだったりしても、それらを連携させてSSOを実現する方法。詳細は後程。

SSOシステムの機能

  • 代行ログイン機能
    • SSOシステムがユーザーに変わって業務システムにログインする
  • セッション管理機能
    • 業務システムへのログインセッションをSSOシステムが一括管理する
    • タイムアウト時間の設定等もSSOシステムが管理する
  • 情報継承機能
    • ユーザーの属性情報をSSOシステムが保存・管理し、業務システムへ渡す機能
    • 個々の業務システムでユーザー属性情報を保存・管理する必要がなくなる
  • アクセス制御機能
    • ユーザーから業務システムの各コンテンツへのアクセス可否を制御する
    • いわゆる認可機能
  • ダイナミックメニューポータル機能
    • SSOシステムへのログイン直後に業務システムへのリンクをメニューとしてポータル画面に表示する機能
    • あなたがアクセスできるツールはこれですよーみたいなもんか
    • ここにSentry、Datadog等々が乗れば良さそう
  • アクセス記録機能
    • 不正アクセスがあったときに、影響範囲をすぐに調査できる
    • 社内で利用されているサービスを可視化できる
  • windows認証との連携機能
    • windows PCにログインしたら、SSOシステムへのログインが不要になる機能

フェデレーション

それぞれ個別に認証機能を持つクラウドやサイト間でのシングルサインオンを実現する方法。この仕組みを使うことによって、パブリッククラウドサービスへの認証を一元管理することができるようになる。

SAML(Security Assertion Markup Language)

OASIS (Organization for the Advancement of Structured Information Standards)が定めた クラウドサービスと企業の間で認証の連携を行うための標準規格。

フェデレーションはパスポートの仕組みに似ている。自社でパスポートを発行し、クラウドサービス側ではパスポートが適切であればサービスへのアクセスを許可する。パスポートを発行する側を Identity Provider(IdP)。 クラウドサービス側を Service Provider(SP). このパスポート自体はSSOの世界では アサーション と呼ぶ。

SPがIdPを信頼できれば、そのIdPから生成されるアサーションの確認を、ユーザーの本人確認とみなす。一度発行されたアサーションは特定のSPへのログインにしか使えない。

SAMLによる実際の処理の流れ

  • ユーザーがIdPにアクセスしてパスワード等でユーザーへの認証を行う
  • IdPがアサーションを発行する
  • アサーションがWebブラザを介してSPに提示される
  • アサーションの正当性と信頼したIdPによる発行かどうかをSPが確認する
  • SPがユーザーに利用を許可する

参考資料

AWS Startup Daysでちょっとお話ししてきました

3/12 日のAWS Startup Daysというイベントでちょっとお話しさせていただきました。

aws.amazon.com

お話しさせて頂いた内容は Serverlessで作るchatbot。 弊社SpeeeにおけるServerlessの事例と一緒にお話しさせていただきました。

github.com

発表資料はこちらになります。ここ細かく知りたいっ!!!とかあれば、ブログで色々書いてくので、コメント頂ければ幸いです。

speakerdeck.com

LocalでDynamoDBを起動する

モチベーション

aws lambdaの開発において、開発効率の向上のためにlocalで動作が確認できるようにしたい。 そのためには、localでDynamoDBが動くようにしたい。ここでは、そのために必要な方法について記述する。

DynamoDBの起動

DynamoDB ローカル (ダウンロード可能バージョン) のセットアップ - Amazon DynamoDB

このページから、DynamoDBデータベースをDLします。 そして、下記のコマンドでDynamoDBデータベースを起動します。

# dynamoDB起動コマンド
java -Djava.library.path=./db/DynamoDBLocal_lib -jar ./db/DynamoDBLocal.jar -sharedDb

僕は、下記のようなコードを用意してコマンドで実行できるようにしました。

#!/bin/sh

CMDDIR="$(dirname "$(perl -e 'use Cwd "abs_path";print abs_path(shift)' "$0")")"

case "${1:-}" in
  deploy) shift 1; exec "$CMDDIR/prpolice-deploy" "$@";;
  dynamo) shift 1; exec "$CMDDIR/prpolice-dynamo" "$@";;
  local) shift 1; exec "$CMDDIR/prpolice-local" "$@";;
  setup) shift 1; exec "$CMDDIR/prpolice-db-setup" "$@";; # 今回追加したやつ
  *)
    echo 'Usage: prpolice <subcommand> [<args>]'
    echo
    echo 'Subcommands of pfchecker are:'
    echo '  deploy: deploy lambda function'
    echo '  dynamo: run local dynamo db'
    echo '  local: call lambda in local condition'
    echo '  setup: create local dynamodb table'
    echo
    ;;
esac
#!/bin/sh
cd "$(dirname "$(perl -e 'use Cwd "abs_path";print abs_path(shift)' $0)")"
cd ../
java -Djava.library.path=./db/DynamoDBLocal_lib -jar ./db/DynamoDBLocal.jar -sharedDb

Local DynamoDBにAWS CLIからアクセス

aws-cliを利用する場合は簡単です。 下記のoptionを設定すれば、
大抵のコマンドは動きます。 --endpoint-url http://localhost:8000

aws dynamodb create-table \
  --table-name User \
  --attribute-definitions \
    AttributeName=id,AttributeType=S \
  --key-schema AttributeName=id,KeyType=HASH \
  --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 \
  --endpoint-url http://localhost:8000

ちなみに、aws cliのhelpを読んでも書いてありませんでした。

ローカルエンドポイントの設定 - Amazon DynamoDB

Local DynamoDBにaws-sdkからアクセス

import AWS from "aws-sdk";

const dynamo = new AWS.DynamoDB({
  endpoint: "http://localhost:8000",
  region: "ap-north-east1"
});

設定としてはこれだけです。下記のscriptを実行すると、LocalのDynamoDBにアクセスすることができるようになります。

# sample.ts
import AWS from "aws-sdk";

const dynamo = new AWS.DynamoDB({
  endpoint: "http://localhost:8000",
  region: "ap-north-east1"
});

const params = {
  TableName: "User",
  Item: { Id: { S: "SlackID" }, github: { S: "GitHubID" } }
};

const getParams = {
  Key: {
    Id: {
      S: "SlackID"
    }
  },
  TableName: "User"
};

// put Item
dynamo.putItem(params, (err, data) => {
  console.log(err);
  console.log(data);
});


// get Item
dynamo.getItem(getParams, (err, data) => {
  console.log(err);
  console.log(data);
});

まとめ

Local DynamoDBの実行に関して、ソフトウェアのダウンロードから、AWS CLI / aws-sdk でのアクセス方法について記載しました。案外簡単にできました。

その他

今考えたら Homebrewを使ってDLすればよかったですね。

brewformulas.org

mongoDBでテーブル作成、アイテム作成、アイテム取得をやってみる

モチベーション

GitHub - selmertsx/serverless-prpolice

自分が作っているサービスでmongoDBを触ることになったので、下記の項目を理解したい。

  • mongoDBの概要
  • mongoDBにおける基本的な操作の把握
    • Tableの作成
    • Itemの作成
    • Itemの取得

mongoDBの概要

  • Table、Item、Attributesの3つで出来てる
  • TableはItemの集合
  • ItemはAttributesの集合

DynamoDBはプライマリキーを使用して、テーブルのItemを一意に識別する。Itemの一部には、入れ子のAttributesを作ることができる。

{
    "PersonID": 1, #こいつがプライマリキー
    "LastName": "Sample",
    "Address": {
        "Street": "123 Hoge"
    }
}

パーティションキー

一つの属性で構成されたシンプルなプライマリキー。パーティションキーの値を内部ハッシュ関数への入力として利用する。これによって、Itemを一意に識別できるようにする。

複合プライマリキー

最初の属性であるパーティションキーと2番めの属性のソートキーによって構成される。同じパーティションキーを持つ全てのItemは、ソートキー値でソートされて保存される。同じパーティションキーのItemは複数存在しても、同じパーティションキー & ソートキーのItemが複数存在することは許されない。

mongoDBにおける基本的な操作

Tableの作成

aws dynamodb create-table \
  --table-name User \
  --attribute-definitions \
    AttributeName=id,AttributeType=S \
  --key-schema AttributeName=id,KeyType=HASH \
  --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 \
  --endpoint-url http://localhost:8000
  • Attributesとして定義したものには KeySchemaを設定しなければならない
  • 複合プライマリキーの場合、二番めキーのKeyTypeはRANGEとなる
  • Attributesとして定義しなくとも、値を格納することはできる

Itemの作成

import AWS from "aws-sdk";

const dynamo = new AWS.DynamoDB({
  endpoint: "http://localhost:8000",
  region: "ap-north-east1"
});

const putParams = {
  TableName: "User",
  Item: { Id: { S: "SlackID" }, github: { S: "GitHubID" } }
};

dynamo.putItem(putParams, (err, data) => {
  console.log(err);
  console.log(data);
});

Itemの取得

import AWS from "aws-sdk";

const dynamo = new AWS.DynamoDB({
  endpoint: "http://localhost:8000",
  region: "ap-north-east1"
});

const getParams = {
  Key: {
    Id: {
      S: "SlackID"
    }
  },
  TableName: "User"
};

dynamo.getItem(getParams, (err, data) => {
  console.log(err);
  console.log(data);
});

その他リンク集

期限切れになった項目を自動的にテーブルから削除することも可能 https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/TTL.html

ベストプラクティス集 https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/BestPractices.html

ふつうのLinuxプログラミング(4章)

学べること

ディレクトリのパーミッションの挙動は下記の通り。

  • read => ファイル一覧が取れる
  • write => ディレクトリの中にファイルを作れる
  • execute => そもそもファイルにアクセスできない

僕達がコマンドラインとして認識しているのは、shellとterminalという2つの構成要素から成る。端末はファイルとして表現される。

※ 端末については、こいつをファイルと見立てて入出力のストリームを作成することで、コンピューターと人間がやりとりできるようにするもの。というくらいの認識でいる。

以下実験内容

ファイルのパーミッション

普通につくると、パーミッションは実行権限を持たない

$ echo "echo 'hello'" > sample.sh
$ ls -l
total 8
-rw-r--r--  1 shuhei_morioka  admin  13  2 14 21:42 sample.sh
# shell command が sample.sh を読み込んで実行している
$ sh sample.sh
hello
# パイプを使ってストリームを作成し、sh コマンドに渡して実行することも可能
$ cat sample.sh | sh
hello
# 実行権限が無いので実行不可
$ ./sample.sh
zsh: permission denied: ./sample.sh
# sample.sh から読み込み権限を抜くと、shell scriptで実行不可
$ chmod -r sample.sh
$ sh sample.sh
sh: sample.sh: Permission denied
# 実行権限を付与すると、./sample.sh 単体で実行可能
$ chmod +x sample.sh
$ ls -l
total 8
-rwxr-xr-x  1 shuhei_morioka  admin  13  2 14 21:42 sample.sh
$ ./sample.sh
hello

ディレクトリのパーミション

読み込み権限が無いディレクトリの場合

$ mkdir sample_dir
$ ls -l
total 8
-rwxr-xr-x  1 shuhei_morioka  admin  13  2 14 21:42 sample.sh
drwxr-xr-x  2 shuhei_morioka  admin  68  2 14 22:33 sample_dir
$ chmod -x sample_dir
$ ls -l
total 8
-rwxr-xr-x  1 shuhei_morioka  admin  13  2 14 21:42 sample.sh
drw-r--r--  2 shuhei_morioka  admin  68  2 14 22:33 sample_dir
$ cd sample_dir
cd: permission denied: sample_dir

実行権限が無いディレクトリの挙動

➜  permission chmod +x sample_dir
➜  permission touch sample_dir/hoge
➜  permission ls -l sample_dir
total 0
-rw-r--r--  1 shuhei_morioka  admin  0  2 14 22:35 hoge
➜  permission chmod -x sample_dir
➜  permission ls -l
total 8
-rwxr-xr-x  1 shuhei_morioka  admin   13  2 14 21:42 sample.sh
drw-r--r--  3 shuhei_morioka  admin  102  2 14 22:35 sample_dir
➜  permission ls -l sample_dir
ls: hoge: Permission denied

端末の実験

今の端末のファイルを確認する方法

➜  permission tty
/dev/ttys000

別の端末から、/dev/ttys000 に文字列を入力すると

➜  ~ tty
/dev/ttys003
➜  ~ echo "hogehoge\nhogehoge" > /dev/ttys000

/dev/ttys000 の端末に出力される。

# /dev/ttys000 の端末
➜  permission hogehoge
hogehoge

TypeScript 2.7での esModuleInteropをつけたコンパイルの挙動

モチベーション

GitHub - selmertsx/serverless-prpolice

個人プロダクトの開発中に、TypeScriptのコードが buildすると実行できるのに、Jestのテストだけコケるという問題が発生した。その原因を調査したかった。

前提条件

"typescript": "^2.7.1"

問題の内容

consoleの内容

# 呼び出し元のコード
import { GitHubApi } from "@octokit/rest";
const github = new GitHubApi();
github.authenticate({...})
$ yarn test
yarn run v1.3.2
$ jest
 PASS  __tests__/github.spec.ts
  pullRequests
    ✓ get (850ms)
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        4.1s, estimated 6s
$ bin/prpolice local get_pr
(node:1) UnhandledPromiseRejectionWarning: 
Unhandled promise rejection (rejection id: 1): TypeError: rest_1.default is not a constructor

調査

# buildされたコード
Object.defineProperty(exports, "__esModule", { value: true });
var rest_1 = require("@octokit/rest");
var pull_request_1 = require("./pull_request");
var GitHub = (function () {
    function GitHub(owner, repo) {
        this.github = new rest_1.default();

https://github.com/octokit/rest.js/blob/master/index.js

module.exports = GitHubApi
function GitHubApi (options) {
  ....
end

Compiler Options · TypeScript

Emit importStar and importDefault helpers for runtime babel ecosystem compatibility and enable --allowSyntheticDefaultImports for typesystem compatibility.

# --esModuleInterop をつけてコンパイルされたコード
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
}
Object.defineProperty(exports, "__esModule", { value: true });
var rest_1 = __importDefault(require("@octokit/rest"));
var pull_request_1 = require("./pull_request");
var GitHub = (function () {
    function GitHub(owner, repo) {
        this.github = new rest_1.default();
        this.owner = owner;
        this.repo = repo;
    }
# コンパイルされたコードの挙動の確認
$ node
> const a = { "default": require("@octokit/rest") }
undefined
> a.default
[Function: GitHubApi]
> a.default()
{ hook:
   { [Function: bound register]

このコードで、jestも sam localのテストも動くことが確認できました。

20180208の学び

最初に

これは個人プロダクトで学んだ内容をまとめたもの。

GitHub - selmertsx/serverless-prpolice

忘れる前に書いておくけれども、後でちゃんと整理する

学んだ内容

AWS

  • cloudformation でlambdaをdeployする前に、awsのs3にbucketを作っておかなければならない
aws s3api create-bucket --bucket prpolice --create-bucket-configuration LocationConstraint=ap-northeast-1
# まずはAPI Gatewayの Method Request レイヤーで受け取る
HTTP Method: POST, Resource Path: /index
Method request path: {}
Method request query string: {}
Method request headers: {Accept=*/*, CloudFront-Viewer-Country=US, CloudFront-Forwarded-Proto=https, CloudFront-Is-Tablet-Viewer=false, CloudFront-Is-Mobile-Viewer=false, User-Agent=Slackbot 1.0 (+https://api.slack.com/robots), X-Forwarded-Proto=https, CloudFront-Is-SmartTV-Viewer=false, Host=xxx, Accept-Encoding=gzip,deflate, X-Forwarded-Port=443, X-Amzn-Trace-Id=Root=xxx, Via=1.1 xxx.cloudfront.net (CloudFront), X-Amz-Cf-Id=xx==, X-Forwarded-For=xxxx, CloudFront-Is-Desktop-Viewer=true, Content-Type=application/json}
Method request body before transformations:
{
    "token": "xxxx",
    "challenge": "xxx",
    "type": "url_verification"
}

# その後、Integration Requestにリクエストを流す
Endpoint request URI: https://lambda.ap-northeast-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:xxx:function:prpolice-xxx/invocations
Endpoint request headers: {x-amzn-lambda-integration-tag=xxx, Authorization=***, X-Amz-Date=20180208T122332Z, x-amzn-apigateway-api-id=b3kfmkg7ig, X-Amz-Source-Arn=xxx, Accept=application/json, User-Agent=xxx, X-Amz-Security-Token=xxx [TRUNCATED]

Slack

20180209にやること

  • とりあえず動くところまで持ってく
  • API Gatewayを含めた動作をいい感じにテストできるようにしたい
    • AWS SAM Local上でテストできるようにしたい