Slide 1

Slide 1 text

株式会社 Flatt Security @pizzacat 8 3 Deno で作る快適な “as Code” プラットフォーム

Slide 2

Slide 2 text

$ whoami pizzacat 8 3 
 Flatt Security ソフトウェアエンジニア セキュリティエンジニアとして Flatt Security に⼊社し、Web‧ Firebase‧クラウドのセキュリティ診断を担当しながら、 
 診断を管理する社内システムの内製にも従事。 
 現在はソフトウェアエンジニアとしてセキュリティ SaaS 
 「Shisho Cloud」の開発に従事し、フロントエンド、バックエンド、 CLI、TypeScript‧Rego コード実⾏基盤などを⼿掛ける。 2

Slide 3

Slide 3 text

Deno で作る快適な “as Code” プラットフォーム セキュリティ SaaS「Shisho Cloud」における 
 「クラウドの設定値検査ルールを TypeScript で書ける」機能の裏側をご紹介 
 3 Policy as Code function allows_ssh_public_access(rule: FirewallRule): boolean { const SSH_PORT = 22; return rule.direction === "INGRESS" && rule.sourceRanges.some(range => range === "0.0.0.0/0") && rule.allowed.some(({ ipProtocol, ports }) => (ipProtocol === "all" || ipProtocol === "tcp" || ipProtocol === "sctp") && ports.some(({ from_, to }) => from_ <= SSH_PORT && SSH_PORT <= to) ) }

Slide 4

Slide 4 text

Deno で作る快適な “as Code” プラットフォーム セキュリティ SaaS「Shisho Cloud」における 
 「クラウドの設定値検査ルールを TypeScript で書ける」機能の裏側をご紹介 4 function allows_ssh_public_access(rule: FirewallRule): boolean { const SSH_PORT = 22; return rule.direction === "INGRESS" && rule.sourceRanges.some(range => range === "0.0.0.0/0") && rule.allowed.some(({ ipProtocol, ports }) => (ipProtocol === "all" || ipProtocol === "tcp" || ipProtocol === "sctp") && ports.some(({ from_, to }) => from_ <= SSH_PORT && SSH_PORT <= to) ) }

Slide 5

Slide 5 text

セキュリティ SaaS 5 Shisho Cloud (シショウクラウド) は、クラウド上のインフラなどの情報資産の⼀元管理‧継続的なセキ ュリティ検査‧リスク管理ができるセキュリティ SaaS です。

Slide 6

Slide 6 text

Shisho Cloud を⽤いたリスク管理の流れ 6 連携 検査 対応

Slide 7

Slide 7 text

「何を」「どのように」検査するのかをコードで表現 →検査内容を柔軟にカスタマイズ‧⾃作可能 
  透明性‧再現性の⾼い検査 Shisho Cloud の特⾊: 全ての検査ルールを “as Code” 7 > > > > > source_range === "0.0.0.0/0" && … source_range !== "X.X.X.X/X" && …

Slide 8

Slide 8 text

課題意識: ⾃動セキュリティ検査の「アラート疲れ」 8 “public-” から始まる 
 バケットは意図して 
 公開しているはず… 要対応 要対応 対応不要

Slide 9

Slide 9 text

const ok = !is_publicly_exposed(bucket) 
 Shisho Cloud なら、検査コードのカスタマイズでアラートの過不⾜を解決 9 公開バケットの社内ルール: バケット名を “public-XXX” にする || bucket.name.startsWith("public-") 真に対応すべきアラートに 
 集中できる ❗

Slide 10

Slide 10 text

Shisho Cloud なら、検査コードのカスタマイズでアラートの過不⾜を解決 1 0 公開バケットの社内ルール: バケットに “public” ラベルをつける 多様な「社内ルール」に合わせて 
 カスタマイズ可能 ❗ const ok = !is_publicly_exposed(bucket) 
 || bucket.labels.some(label => 
 label.key === "public")

Slide 11

Slide 11 text

セキュリティ検査ルールの素朴な “as Code” for (const project of googleCloudProjects) { const client = new Storage({ credentials: /* ... */, }) const [buckets] = await client.getBuckets() for (const bucket of buckets) { const is_publicly_exposed = bucket.acl.some(rule => /* ... */) if (is_publicly_exposed) { await slack.chat.postMessage({ text: `${bucket.name} is public` // ... 1 1 認証 設定値取得 合否判定 通知 コードを定期実⾏するインフラ クラウドや Slack の認証情報管理

Slide 12

Slide 12 text

セキュリティ検査ルールのスマートな “as Code” — 1 2 query { googleCloud { projects { cloudStorage { buckets { name acl { entity role } 「どんな情報を検査するか」 
 as GraphQL query 実⾏インフラ‧認証情報管理などは 
 Shisho Cloud にお任せ export default (data: QueryResponse) => data.googleCloud .projects ./* ... */ .buckets .map(bucket => ({ ok: !bucket.acl.some(rule => /* ... */) })) 「どのように検査するか」 
 as TypeScript code ❗ 宣⾔的なコード化

Slide 13

Slide 13 text

検査結果 Shisho Cloud における⾃動検査の全体像 1 3 引数: GraphQLの結果 返り値: OK/NG 判定 GraphQL 
 サーバー TS 実⾏基盤 通知システム ①設定値取得 ②TS コード実⾏ ③アラート通知 設定値 データ

Slide 14

Slide 14 text

「クラウドの設定値検査ルールを TypeScript で書ける」 • セキュリティ検査ルールを as Code すれば、柔軟なカスタマイズが可能に • Shisho Cloud では、検査ルールを 
 GraphQL クエリと TypeScript コードでコード化 → 柔軟性は保ちつつ、宣⾔的で簡潔に書ける 1 4

Slide 15

Slide 15 text

検査ルール as Code を⽀えるシステム

Slide 16

Slide 16 text

検査結果 Shisho Cloud における⾃動検査の全体像 1 6 引数: GraphQLの結果 返り値: OK/NG 判定 GraphQL 
 サーバー TS 実⾏基盤 通知システム ①設定値取得 ②TS コード実⾏ ③アラート通知 設定値 データ Shisho Cloud サーバーサイド

Slide 17

Slide 17 text

検査ルール as Code を⽀えるシステム 1 7 ユーザーのローカル環境 GraphQL クエリ まとめて 
 アップロード } TS 型定義 TS 検査ロジック 依存モジュール 型定義 
 コード⽣成 bundler

Slide 18

Slide 18 text

検査ルール as Code を⽀えるシステム • クラウド設定値を取得する GraphQL サーバー • TS コードを実⾏する TS 実⾏基盤 • GraphQL クエリ→ TS 型定義コード⽣成器 • アップロード時に依存モジュールをまとめる bundler 1 8

Slide 19

Slide 19 text

検査ルール記述における GraphQL + TS の魅⼒

Slide 20

Slide 20 text

セキュリティ検査を、宣⾔的に書けるコンポーネントに分解 2 0 query { googleCloud { projects { cloudStorage { buckets { name acl { entity role } 「どんな情報を検査するか」 
 as GraphQL query export default (data: QueryResponse) => data.googleCloud .projects ./* ... */ .buckets .map(bucket => ({ ok: !bucket.acl.some(rule => /* ... */) })) 「どのように検査するか」 
 as TypeScript code

Slide 21

Slide 21 text

型システムの恩恵: 考慮漏れのない検査ロジック 「どのような設定値がありうるか」が GraphQL スキーマからわかる 2 1 enum GoogleCloudStorageBucketACLRole { OWNER READER WRITER } 例:「任意のリクエスト元が書き込み可能」 
   な GCS バケットを禁⽌したい ❗ WRITER だけでなく 
 OWNER の ACL もチェックすべき

Slide 22

Slide 22 text

型システムの恩恵: 考慮漏れのない検査ロジック ケースの考慮漏れをしてしまっても、型検査で気付ける 2 2 case: "OWNER" が漏れている ❗

Slide 23

Slide 23 text

TS の魅⼒ — もう⼀つのポリシー記述⾔語 Rego と⽐べて • 多くのエンジニアがすでに慣れている • 「どのように検査したいか」 
 考えをスムーズにコード化できる • 便利な型推論‧型検査 2 3 is_exposed_publicly(acl) { rule := acl[_] rule.entity == "allUsers" } else { rule := acl[_] rule.entity == "allAuthenticatedUsers" } else = false Rego のコード例

Slide 24

Slide 24 text

TS の魅⼒ — 他の汎⽤プログラミング⾔語と⽐べて ネストしたオブジェクトの型定義が⾒やすい 2 4 export type Input = { googleCloud: { projects: Array<{ cloudStorage: { buckets: Array<{ name: string, acl: Array<{ entity: string, role: | "OWNER" | "READER" | "WRITER", }>, type Input struct { GoogleCloud GoogleCloud } type GoogleCloud struct { Projects []GoogleCloudProject } type GoogleCloudProject struct { CloudStorage GoogleCloudProjectCloudStorage } type GoogleCloudProjectCloudStorage struct { Buckets []GoogleCloudProjectCloudStorageBuckets } type GoogleCloudProjectCloudStorageBuckets struct { Name string ACL []GoogleCloudProjectCloudStorageBucketsACL IamPolicy GoogleCloudProjectCloudStorageBucketsIamPolicy } type GoogleCloudProjectCloudStorageBucketsACL struct { Entity string Role GoogleCloudProjectCloudStorageBucketsACLRole } type GoogleCloudProjectCloudStorageBucketsACLRole string Go

Slide 25

Slide 25 text

TS の魅⼒ — 他の汎⽤プログラミング⾔語と⽐べて null ハンドリングを簡潔に書ける 2 5 const x = data?.method?.(0)?.field ?? false x := false if data != nil && data.Method != nil { res := (*data.Method)(0) if res != nil { x = res.Field } } Go

Slide 26

Slide 26 text

GraphQL + TS の魅⼒ — コード実⾏基盤の提供者視点から V 8 で信頼できない JS を軽量に実⾏ …… ⻑くて数秒程度のコード実⾏に適する cf. Cloud fl are Workers (workerd) もコンテナ‧仮想マシンではなく 
  V 8 の隔離機構を⽤いてコード実⾏ 
  →起動オーバーヘッド‧使⽤リソースの削減 2 6

Slide 27

Slide 27 text

GraphQL + TS の魅⼒ — コード実⾏基盤の提供者視点から 2 7 素朴な as Code → 外界とのやり取り → attack surface が多い 外界との能動的やり取りがない GraphQL 
 の結果 検査結果 → 関数引数 返り値 データ加⼯のみ 😈 😶

Slide 28

Slide 28 text

検査ルール as Code を⽀えるシステムの裏側

Slide 29

Slide 29 text

検査ルール as Code を⽀えるシステム (再掲) • クラウド設定値を取得する GraphQL サーバー • TS コードを実⾏する TS 実⾏基盤 • GraphQL クエリ→ TS 型定義コード⽣成器 • アップロード時に依存モジュールをまとめる bundler 2 9

Slide 30

Slide 30 text

TS 実⾏基盤 Deno ベースの Rust 製サーバー 3 0 Deno CLI deno_core deno_ast deno_fs … … deno_core deno_ast TS 実⾏ 基盤 Rust ライブラリ

Slide 31

Slide 31 text

なぜ Deno ベースなのか • セキュリティを意識した設計 • Deno CLI を⼊れるだけで TS 対応‧test‧linter‧formatter など⼀式揃う • Rust ライブラリを使って実装できる • コピーをなるべく避けつつも、メモリバグを⽣みにくい 3 1

Slide 32

Slide 32 text

Deno 以外の⾯⽩そうなランタイム • workerd (Cloud fl are Workers) • V 8 Isolate による隔離実⾏ • LLRT (AWS Lambda) • JIT のない軽量エンジン QuickJS を採⽤し、オーバーヘッドやシステムの複雑性を 回避 • WinterJS • WASM 上で JS ランタイムが動く 3 2

Slide 33

Slide 33 text

$ deno run をそのまま TS 実⾏基盤に使うと実は脆弱 3 3 import * as remoteData from 'https://metadata.internal/token.json'; import * as localFile from './foo.json'; export default function() { return { remoteData, localFile }; } 😈 import ⽂で内部ネットワーク‧ローカルファイルを盗み出せる

Slide 34

Slide 34 text

Shisho Cloud の TS 実⾏基盤と $ deno run との主な違い 機能を削ぎ落とし、検査ルール実⾏に特化したシンプルな実⾏基盤へ • Ops (外界とのやり取り⼿段) のほとんどを外す • ファイルアクセスや fetch など不可 • module loader はメモリ上のモジュール群のみからロードする • import を悪⽤したファイル‧ネットワークアクセスを回避 Deno の Rust ライブラリは割と疎結合で、機能を削ぎ落としやすい 3 4

Slide 35

Slide 35 text

import のあるコードの扱い 3 5 // main.ts import { foo } from './local_module.ts' import { bar } from ‘https://remote.example/module.ts' export default function(data: QueryResult) { // ... } main.ts を Shisho Cloud 上で実⾏するには、 
 依存モジュール local_module.ts のアップロードも必要 → 全ての依存モジュールを bundler でまとめてアップロード

Slide 36

Slide 36 text

bundler ⾃作のこだわり: あえて単⼀ JS ファイルにまとめない esbuild のように単⼀の JS ファイルにまとめるのではなく、 
 全ての依存モジュールをそのまま Map にした YAML ⽣成 … Deno 内部ライブラリを⽤いて⾃社開発 3 6 modules: file:///main.ts: | import { foo } from './local_module.ts' import { bar } from 'https://remote.example/m export default function(data: QueryResult) { // ... } file:///local_module.ts: | export function foo() {/* ... */}

Slide 37

Slide 37 text

bundler ⾃作のこだわり: あえて単⼀ JS ファイルにまとめない — 理由 Shisho Cloud の Web 画⾯上で、検査コードを軽く確認‧少し書き換える際に 
 読み書きしやすいコードであるべき 3 7 modules: file:///main.ts: | import { foo } from './local_module.ts' import { bar } from 'https://remote.example/m export default function(data: QueryResult) { // ... } file:///local_module.ts: | export function foo() {/* ... */} function foo() { ... } function bar() { ... } function main_default(data) { ... } export { main_default as default }; 型やコメントが消える 従来の bundler bundler for Shisho Cloud 書いたコードをそのままアップ

Slide 38

Slide 38 text

TS 実⾏基盤‧bundler づくりの悩みどころ • rusty_v 8 (V 8 の Rust バインディング) のドキュメントもっと充実してほしい • Deno の Rust ライブラリはまだ安定せず、アプデでたまに破壊的変更 • 使いたい機能が Deno CLI に密結合している場合もある • MIT ライセンスなのでコピペしても良いが、メンテが⼤変 … Deno がまだ若い技術ゆえのペイン 3 8

Slide 39

Slide 39 text

GraphQL → TS 型定義⽣成器 Rust 向け型定義⽣成器 graphql_client を 
 fork して TS 向けに改変 …将来の多⾔語対応を⾒据え、 
  複数⾔語の型定義を⽣成できる 
  単⼀の Rust 製ツールを⽬指す 3 9 export type Input = { /** All data from Google Cloud integr googleCloud: { /** Projects in Google Cloud */ projects: Array<{ /** Data on Google Cloud Storage cloudStorage: { /** All Google Cloud Storage bu buckets: Array<{ /** The bucket name */ name: string, /** The list of access contro acl: Array<{ entity: string, role: | "OWNER" | "READER" | "WRITER", }>, /** The IAM policy of the Goo iamPolicy: { 型定義 
 コード⽣成 Rego 他⾔語

Slide 40

Slide 40 text

型定義⽣成のこだわり: 読みやすさ第⼀ 4 0 export type Input = { /** All data from Google Cloud integr googleCloud: { /** Projects in Google Cloud */ projects: Array<{ /** Data on Google Cloud Storage cloudStorage: { /** All Google Cloud Storage bu buckets: Array<{ /** The bucket name */ name: string, /** The list of access contro acl: Array<{ entity: string, role: | "OWNER" | "READER" | "WRITER", }>, /** The IAM policy of the Goo iamPolicy: { • 単⼀の⼤きなネストした型定義を⽣成 • ⼊⼒データの全体構造が⼀⽬でわかる • 型だけでなく doc comment も⽣成 • GraphQL クエリ‧スキーマを⾒返す必要なし

Slide 41

Slide 41 text

型定義⽣成のこだわり: 読みやすさ第⼀ 4 1 export type Input = { /** All data from Google Cloud integr googleCloud: { /** Projects in Google Cloud */ projects: Array<{ /** Data on Google Cloud Storage cloudStorage: { /** All Google Cloud Storage bu buckets: Array<{ /** The bucket name */ name: string, /** The list of access contro acl: Array<{ entity: string, role: | "OWNER" | "READER" | "WRITER", }>, /** The IAM policy of the Goo iamPolicy: { • 情報後出しを避け、上から下に読めるように • T[] ではなく Array • T | null ではなく Nullable

Slide 42

Slide 42 text

まとめ

Slide 43

Slide 43 text

まとめ • Shisho Cloud ではセキュリティ検査を GraphQL + TS でコード化 • ユーザーは検査ルールを柔軟に‧快適にカスタマイズできる • as Code 基盤のために、いろいろ⾃社開発した • ⾃社開発なら、細部にまでこだわれる • JS/TS の深い領域に携われて楽しい! 4 3 まだまだ語り⾜りないので 
 懇親会でぜひお話ししましょう!