Slide 1

Slide 1 text

GoでTCP Proxyを実装してみよう 株式会社サイバーエージェント AI事業本部 黒崎 優太 (@kuro_m 88 ) CA.go LT会

Slide 2

Slide 2 text

ࠇ࡚ ༏ଠ גࣜձࣾαΠόʔΤʔδΣϯτ AIࣄۀຊ෦ @kurochan @kuro_m88 αΠόʔΤʔδΣϯτ CTO౷ׅࣨ

Slide 3

Slide 3 text

TCP Proxyを作った背景

Slide 4

Slide 4 text

TCP Proxyを作ったきっかけ https://speakerdeck.com/kurochan/securing-development-environments-using-cloud fl a 7

Slide 5

Slide 5 text

WireGuardとOpenID Connectの連携 https://speakerdeck.com/kurochan/wireguardtoopenid-connectfalselian-xi-wogoteshi-zhuang-sitemita

Slide 6

Slide 6 text

WireGuardとOpenID Connectの連携 • 実装してみて、 一 応動いたが… • 便利だったようで 一 気に数 十人 使うようになった • 実装に必要な前提知識が意外と多くてメンテを誰かに引き継げる気がしなかった • WireGuard、OIDC CIBA Flow、nftables、netlink、Go 言 語でのこれらの動的操作 • 固定IPだけに頼るのはイケてない • せっかく社内に内製IdPがあるので柔軟な認可の制御もしたい • VPC内アクセスとかもしたい

Slide 7

Slide 7 text

脱WireGuard • WireGuardの導 入 によって開発プロジェクト 用 のVPNの需要は把握 • もっとやりたいことが増えてきた • 通信先によってアクセス元IPアドレスを変えたい • 人 や役割によってアクセス可否を変えたい(認可をちゃんとやりたい) • 通信先によってVPNを切り替えるのが 面 倒 • ちょうどいい製品を探し始めた

Slide 8

Slide 8 text

Cloud fl are Zero Trust • いろんな機能があって充実している • アイデア次第で 色 々できそう • その分最初は使い 方 を迷うかも…? • スマホ対応は必須だった • スマホアプリの開発で開発環境のAPIのアクセス制御もしたいのでブラウザ対応だけではダメ • APIで操作するためのクライアントライブラリが公式で 用 意されている • https://github.com/cloud fl are/cloud fl are-go • 安い!!!!!! • $ 8 /user • 現在90ユーザ使っているので、 $8 x 9 0 = $ 720 /month • 通信量課 金 なし!!!!!!

Slide 9

Slide 9 text

Cloud fl are Zero Trustの導 入 • 構成の概要

Slide 10

Slide 10 text

cloud fl aredに中継する • cloud fl aredで100.64.0.1をlistenする • Cloud fl are Zero Trustの設定で100.64.0.1/24をcloud fl aredに転送する • nginxで本来の宛先にTCP Proxy

Slide 11

Slide 11 text

cloud fl aredに中継する • cloud fl aredで100.64.0.1をlistenする • Cloud fl are Zero Trustの設定で100.64.0.1/24をcloud fl aredに転送する • nginxで本来の宛先にTCP Proxy ↑↑今回の本題!↑↑

Slide 12

Slide 12 text

TCP Proxyを実装してみよう

Slide 13

Slide 13 text

TCP Proxyとは • 中継するもの、 土 管(パイプ)みたいなイメージ

Slide 14

Slide 14 text

もうちょっと掘り下げる • Proxy 自身 もサーバとクライアントから構成されていることがわかる

Slide 15

Slide 15 text

TCP Server

Slide 16

Slide 16 text

TCPの3-Way Handshake • Wikipediaより • 基本的にはOS側で処理してくれるので、アプリを実装する 人 は コネクションを"accept()"すればいい

Slide 17

Slide 17 text

もうちょっと掘り下げる • OS側でACK状態になっている通信をアプリでAcceptすることを繰り返す

Slide 18

Slide 18 text

もうちょっと掘り下げる • サーバは複数コネクション 生 成すれば複数クライアントと同時に通信可能

Slide 19

Slide 19 text

TCP Serverの実装 • みていきましょう

Slide 20

Slide 20 text

Listen 終了処理 Acceptするループ

Slide 21

Slide 21 text

Listen • 特定のポートをTCPで待ち受ける(acceptできる状態にする) • contextをとらないので終了処理を実装する必要アリ

Slide 22

Slide 22 text

Acceptするループ • "accept()"するループはgoroutineで独 立 させる • 後述する終了処理のため • 無限ループしてコネクションを待ち受ける • コネクションが貼れたら別のgoroutine(p.handleConn())に処理させる • Acceptのループでコネクションを処理すると新規のコネクションが受けつけられない!

Slide 23

Slide 23 text

終了処理 • contextがキャンセルされたら新規にacceptできないようにする • 新規にacceptできないだけで既に確 立 したコネクションはそのまま • Acceptするループはこれによりaccept()不能になるため、 • エラーになってループを抜ける

Slide 24

Slide 24 text

TCP Client

Slide 25

Slide 25 text

TCP Clientの実装 • サーバ側に 比 べると仕事が少ない • コネクションは最 大 でも10分に限定 • コネクションのリーク防 止 • 10分で切られて困る場合はもう少し丁寧に実装する

Slide 26

Slide 26 text

Proxyする

Slide 27

Slide 27 text

Proxyの実装(ServerとClientをつなぐ) • みていきましょう

Slide 28

Slide 28 text

終了処理 Server → Clientのプロキシ Client → Serverのプロキシ

Slide 29

Slide 29 text

TCPは双 方 向ストリーミング TCP 送信 受信 送信 受信

Slide 30

Slide 30 text

双 方 向の通信をつなぐ • コネクションが続く限りデータをコピーする • 何かしらの理由でコピーできなかった場合はプロキシ不能なので サーバ側もクライアント側もコネクションを閉じる

Slide 31

Slide 31 text

データのコピー • 読んだデータをそのまま書き込むのをループでひたすら繰り返す • これがProxy

Slide 32

Slide 32 text

終了処理 • contextが終了したらコネクションを切断する • コネクションをハンドリングしているgoroutineがエラーで終了する

Slide 33

Slide 33 text

全体像

Slide 34

Slide 34 text

全体像 • contextの流れ • cancelが伝搬する

Slide 35

Slide 35 text

おわり • 思ったより簡単なことが伝わるはず(?) • goroutineがあるおかげで難しいことを考えずある程度のパフォーマンスが出る • OSのネイティブスレッドで同じような処理をするとオーバーヘッドがきついかも • 何かしら並 行 処理っぽい実装をすることになるはず • errgroupを使うことでgoroutine leakしにくい実装に • errgroup経由で作成したgoroutineが全て終了しないとシャットダウンできないように実装するため