続 カッコの付け方

AWSを始めとしたクラウドコンピューティング全般と、唯一神emacsにおける()の付け方についてだらだら書きます

ec2 launch templateを参照しようとして you are not authorized to use the launch template

答え

ec2:RunInstances の権限を付与せよ

Launch Template Support - Amazon EC2 Auto Scaling

やろうとしたこと

terraform で ASGを launch templateでやろうとして発生。

resource "aws_launch_template" "lt" {
  name          = "test"
  instance_type = "t3.large"
  image_id      = "xxx"

  vpc_security_group_ids = [
    "sg-xxx"
  ]

}

resource "aws_autoscaling_group" "asg" {
  name                      = "test"
  max_size                  = 20
  desired_capacity          = 0
  min_size                  = 0
  health_check_grace_period = 0
  health_check_type         = "EC2"

  launch_template {
    id      = "${aws_launch_template.lt.id}"
    version = "$Latest"
  }

  availability_zones = [
    "ap-northeast-1a",
  ]

  vpc_zone_identifier = [
    "subnet-xxxx"
  ]

  lifecycle {
    ignore_changes = [
      "desired_capacity",
    ]
  }
}

IAM Policy これだとだめなんで ec2:RunInstances を足してね

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ec2:DeleteLaunchTemplate",
                "ec2:CreateLaunchTemplate",
                "ec2:GetLaunchTemplateData",
                "ec2:DescribeLaunchTemplates",
                "ec2:DescribeLaunchTemplateVersions",
                "ec2:ModifyLaunchTemplate",
                "ec2:DeleteLaunchTemplateVersions",
                "autoscaling:*",
                "ec2:CreateLaunchTemplateVersion",
            ],
            "Resource": "*"
        }
    ]
}

もうちょっとヒントほしい。

visidataでそこそこでかい生データをCLIでさくっと見る

もうこれ系の仕事について18年になる。よその業界の人から見ると「なんかパソコン使ってやる人」にひとくくりにされそうだが、職場によって結構やってることは違った。
そんな中でも振り返ってみるとそこそこでかい単ファイル(csv)を見る機会は結構あって、その都度なんかいいのないかと探していた気がするが多分これでいいんじゃね?

github.com

やりたいこと・できること

  • そこそこの規模のデータをさくっとみたい
  • CLI最強、GUIなんかいらん

そんなひとには良い。公式にもあるが、ガチ分析には向かん。
あと追加の言い訳。今の時代はS3みたいなオブジェクトストレージにそこそこのサイズで切片化してファイルを置くのが正義です。単ファイルのごついCSVは時代に逆行する。切片化したデータを読む場合は、ぱっと調べた感じは visidataはプラグインで対応するらしい。

GitHub - ajkerrigan/visidata-plugins: Custom plugins for https://github.com/saulpw/visidata/

用途は異なるが、大は小を兼ねると考えるならば、切片化されたデータは素直にjupyter/panda(ガチ分析可能)で扱えるようにdask使ったほうが楽やと思う。

docs.dask.org

チュートリアル(+おまけ)

ご丁寧にチュートリアルがついているのでやります。 An Introduction to VisiData — An Introduction to VisiData

とりあえずインストールして vdコマンドが通るようにします。 次に手持ちのデータでもいいですが、何かネタがほしいので、これもチュートリアルについてるデータをダウンロードします。 https://jsvine.github.io/intro-to-visidata/_downloads/83e70cf67e909f3ac177575439e5f3c5/faa-wildlife-strikes.csv

それで vd faa-wildlife-strikes.csv

わお、テンション爆上げ。矢印キーとかでも動かせる。

f:id:iga-ninja:20200426030049p:plain
viewer

この画面も含めたチートシートがあるので、ポイントしておきます。 https://jsvine.github.io/visidata-cheat-sheet/en/

個人的にこれは嬉しいのを書き出すと

  • c カラムを探す、いっぱいカラムがあるときにジャンプ
  • _ カラムの幅を広げる
  • / 選択している絡むの中からパターンマッチでデータを探す、Null以外のデータを見つけたいとかなら.+とかすればよろし
  • [ or ] そのカラムでソートする、あまりにデータがでかいとちょっとしんどい

つぎに何かカラムを選んでSHIFT-fを押せって書いてあるので押すと。

うお、まじか、ヒストグラム出るやんけ。

f:id:iga-ninja:20200426030053p:plain
histg
ま、あとはSHIFT-h ヘルプだけやね。

まとめ&余談

実はこれに似たコンセプトのツールって昔からたくさんあるんです。目的から考えれば、テキストにSQLを打つ的な実現できれば、用途は満たしそうだけど、その手のツールってvisidataほど敷居が低くない、というかビュワーの延長として簡易解析ができるというコンセプトが良いなと思う。機能拡張はプラグインでというのも良い。
最近テキストにSQL系のツールで見たのは、local-dockerとかsqliteRDBMSを動かしてテキストをロードするってやつだった。本物のSQLが通るのは魅力だが、やっぱりいくらdockerとかで複雑さを隠しても、ちょっとわかってらっしゃる人向けな感じはする。まあ、ちょっとわかっている人以外、本物のSQLなどいらないと考えたらこの手のツールは便利ではあるが。

4K 60Hz で出力するまでのハードル [ノートPCユーザー向け]

みなさんリモートワークしてますか? 家では仕事しない主義だったため、お家のPC環境は超プアでしたが、ガンガン散財して絶賛強化中です。 そんな中でも 4Kモニタで リフレッシュレート60Hzで出力するまでの道のりを書いときます。 書き忘れてましたが、ノートPCユーザー向けの話です。デスクトップなら黙ってDisplayPortに繋いどけ。

まとめ

  • 今から買う人で、少々金出してもいい人は USB-C搭載をおすすめする
  • HDMIはモニタ側のポートレベルで30Hz に制限されている安物があるので注意 & HDMIケーブルが問題になることはほとんどない
    • その場合はDisplayPortを使うこと
  • MacBook Pro + DisplayPort の場合は、条件を満たしても30Hzに下げられることがあるが、その場合はmac側の設定で ディスプレイのデフォルト を一回反映してみる

なるべく短く説明

一応、今から買うならどんなのがいいかを最初に書きますが、TNが糞だとかIPSがいいだとか、いやいやPLSだとかのググれば出てくることは端折ります。わかってらっしゃる人向け。
4K60Hzができるか否か、それだけに絞る。

王道はたぶん USB-C搭載

単刀直入に、お金が許す限り2020年の今買うならUSB-C搭載のモデルがいいです。理由はまとめにも書いていますが。

  • 日本で(多分世界でも)主流のHDMIは、ケーブルだけじゃなく、モニタのポートレベルで劣化させている4Kモニタが結構ある。
  • USB-C搭載は、充電もモニタ出力も一本で兼ね備えていて、スッキリする(ノートPC)

デメリットは、まだ出たばっかりなので結構高いことと、給電能力が低く、PCを充電することができないモニタ&PCの組み合わせもあるようです。
また、ゲーム機と接続する場合も確認しておいたほうがいいです、まだ主流なハード側がUSB-Cではないので、もちろんHDMIぐらいはどんなモニタにもついているはずですが、後述する劣化HDMIの可能性もあるかもしれないからです。
また、ケーブルもUSB-Cのケーブルはほんとにピンキリだから、モニターメーカーは絶対ケーブルを付属させるはずなので(手持ちの糞USB-Cケーブルで映らんというクレームを避けるため)4K 60Hz には一番近いとおもう。

ま、結局買ってないから推測なんだけどね。

お値打ちモデルなら、DisplayPort

HDMIだと 4K60Hz が 30Hz になるんですぅ と投稿するとだいたい「ケーブルがHDMI 2.0のやつか確認しろや」と返されるとおもいますが、ここ5年ぐらいに買ったHDMIケーブルを使っているのであればケーブル変えてもたぶん意味ないっす。HDMI1.4と2.0の違いって、物理構造が違うのではなく、規定の速度が出るか出ないかで認定がついてるだけのようです。なので、1.4というカタログスペックで絶対4K60Hzができないとは限りません。やったことないから断定はできないけど、10年前ぐらいのケーブルだと、帯域不足で4Kを映すことすらできないとおもうのでリフレッシュレートどころの話ではなくなるはず。

比較的安価に手に入る4Kモニタは、HDMIポートとDisplayPort側でできる機能を制限してたりします。私の買ったやつがまさにそれで、2018年製のスペック番長な安物です(もちろん4K60Hz対応とデカデカとかいてある)。マニュアル見たところFreeSyncはHDMI接続では有効ではないと書いてあります。例によって「HDMIポートに接続したら30Hzになっちゃったよ」でググると、ケーブル交換しろっていうやつがほとんどだったけど、「イカれてることだが、モニタ側のHDMIポートで制限されてる」という投稿を見つけて、なるほどとおもった。

なんでメーカーは劣化HDMIポートをつけるのか?

HDMIをつけると上納金を納めないといけないらしく、対してDisplayPortにはそれがないその時点でPrimaryはDisplayPortになる(だからデスクトップは黙ってDisplayPort使え)。かといって、HDMI自体をなくしてしまうと流石に売れないだろうということで、劣化でもHDMIをつけているのだろうと推測。メーカーの立場から言うと、「劣化だろうがHDMIついてるんだから、ありがたく思え」ぐらいなんだろうと邪推。知るかそんなこと。

俺の記録

  • まず、買ったモニタ自体にHDMIケーブルは付属しておらず、DisplayPortケーブルのやつだけ入ってた。こういうタイプだとまず劣化HDMIだと思う。
  • で、とりあえず手持ちのHDMIケーブル、規格は1.4までのやつをとりあえずぶっ刺してみたら、30Hzになった。
  • おれは、Win/Linux/Mac(全部ノートね)を持っているが、どれでやってもこれは変わらなかった。 MacHDMI直がないので、USB-C変換コネクタを使ってます。
  • そのあとPremium認定のついたHDMI2.0ケーブルを買ったが、これも全機種NG(30Hz)だった。つまりモニタ側のポートがだめなんで30Hzしか出ない
  • で、DisplayPort->USB-Cのケーブルを買ったところ Win/Linuxは問題なしに60Hzいった
  • Macは30Hz止まりでまじかーと思ったが、前述のMac側のモニター設定でディスプレイのデフォルトに戻すと「あれ、なんかマウスカーソルの残像が少ないぞ!これ60Hzじゃね?」と思ってモニタ側の入力Infoみたら晴れて 60Hzになってた。

30Hzと60Hzって違いわかるのか?

ゲームとかやると流石にわかると思うが、コード書いたりする分には30Hzでもいいんじゃねとは確かに思う。
目が疲れにくいとも言われているが、まだちょっとわからない。マウスカーソルを動かしたときに30Hzはちょっと違和感は感じる。
ここまで書いといてあれだが、俺は30Hzでもなんとか我慢できる。けど60Hzってでたときは嬉しかったなー

sshで手元のコードをサーバ上で動かす(proxychains-ng)

インフラ屋を辞めたので、それなりにコードを書いて走らせる機会が増えました。ローカル上のコードをぱっとサーバ上で動かしたい場合の解決方法を書きます。

tl;dr

  • ssh のポートフォワーディング(ローカルフォワード)には、フォワード先のポートを動的(不定)にするモードあり -D
  • httpには http_proxy環境変数があるが、socks5にはそれほど汎用的なものが無い、特に非http。それをプロセスを1枚かぶせる事によって socks5経由に曲げる proxychain4
  • http proxyが proxyするのはあくまで httpだけ、dnsへの問い合わせなどは、proxy経由とならないが、それすらproxy経由にできる。特にAWSの route53 private hosted zone などを使っているときに効果絶大。

Trampですか?

TRAMP (Transparent Remote Access, Multiple Protocols) emacsサーバー上のファイルをローカルのemacsで編集するものです。 大昔から存在するもので、あくまで emacsでの名称ですが、sublimeでもやっている人がいたので、他のeditorでも実現できるはず。

ただ、これだけローカル開発dockerでテスト&しょっちゅう自動デプロイの時代にサーバー上のファイルを直接いじるってどうなの? インフラ屋だってサーバ上の設定ファイルを直接いじんなって言われてるのに。

というふうに、今もまだ使えるし・使っている人も居ると思うが、時代の流れ的に、もはや多数派にとっても不要なものだとおもう。

ポートフォワードですか?

はい、ポートフォワードです。が、一般的なローカルフォワードだとDNSまで曲げることはできません。それと1本1本接続先を貼ることになるので、そのアプリが触る他のサーバ(DBとか)が1個ならそれほどでもですが、複数なら大変です。

それと、何より面倒なのはローカルフォワードは個人でバインドするポートを色々買えていると思うので、環境変数とかで抽象化して、チームメンバ全員で一致させるのも結構大変やとおもいます。

能書きはこれぐらいで、ズバリどうやるかといえば ssh -D です。sockサーバとして動かすという手法です。ここまでなら結構知名度あるはず

Poor man's VPN via ssh socks proxy

ssh -D と tsocks — 京大マイコンクラブ (KMC)

本題 proxychains-ng について

上記のリンクを見てもらえば解ると思いますが、WebブラウザとかはSock5を通すことができるので楽勝ですが、ブラウザ以外で通すとなると、基本的にアプリ側の実装でSock5を通す設定を作っておく必要が有ります。 早い話、ブラウザ以外でsock5を通すのは自作プログラムしかほぼ無いとなります。

そこでproxychains-ng です。

github.com

その手の界隈では有名だったらしいですが、しばらく更新は途絶えていて Next? Generation として作られたらしい、コマンドは proxychains4 先に上げたとおりDNSも曲げます。

インストールとか設定とか

macなら brewで入ります。Linuxもだいたいパッケージで入りますが、 stretchはちょっとバージョンが古いので、ソースからビルドします。依存がほとんどないので、説明しません。(あとでdockerイメージ晒します)

設定も楽勝です。

strict_chain
proxy_dns 
remote_dns_subnet 224
tcp_read_time_out 15000
tcp_connect_time_out 8000

[ProxyList]
socks5  127.0.0.1 1080

重要な点は最後のIPアドレスとポートです。この場合はsshで立てるsocks5の穴は1080でListenしていることが前提です。 つまり、ssh <host> -D 1080

使い方

proxychains4 curl ifconfig.meとかで使います。 打ちたいコマンドの頭に proxychains4 と書くだけでOK

http-client以外で試してみる

http通信ならブラウザで方がつくので、ほとんどの人がやりたいのはこっちのはず。例えばmysqlとかなら proxychains4 mysql -u <user> -h <ssh接続先絡みたFQDN> -p のような感じで単純に前に proxychains4と入れて呼び出します。これだけでOK

macの場合はbrewのバイナリに注意

ライブラリアクセスに割り込んで、接続先を曲げるという方法を取っているので、brew由来でないものを、brewのproxychainsで曲げることはできないです。ズバリのパターンは rbenvとかでmac上でソースビルドしたrubyとかは通りません。

マニアックなのだとubuntuのsnapとかも無理です。Windowsは知りませんが、msysでビルドしたものを別経由でビルドしたproxychainsで通すことはできないでしょう。ともかく動かそうとするアプリのバイナリと、proxychainsのバイナリが同じシステム(厳格な意味で)に居ないとだめです。

dockerで使うには

同一システムにproxychainsと実行したいバイナリを載せる = コンテナ分離はできないとなります。sshは分離可能で、ホストのsshを使う方法とssh-clientだけ分離する方法があります。ssh-client, proxychains, 対象アプリを全部1コンテナに突っ込む方法もありはありです。 まとめて3パターン

  • 全部1コンテナに突っ込む
  • proxychains + 対象アプリ で1コンテナ、ssh-clientで1コンテナ でコンテナ間通信
  • proxychains + 対象アプリ で1コンテナ、ssh-clientはホストのものをつかう

proxychains install

debianベースはbusterからはaptで普通に入ります。stretchはとりあえずビルドしたバイナリが入ったのを晒します。マルチステージビルドとかで適当につかってください。/usr/localとかに入っているはず https://hub.docker.com/repository/docker/himaoka/proxychains-ng/general

alpineもapkで普通に入ります。

設定・というよりホストのさし方

設定ファイルは上の方で書いたのとほぼ同じですが、コンテナの中からみたホストのIPアドレスmac固有の問題、そしてsshのローカル側Listenに考慮が必要です。 まず -D だけでsshでトンネル貼ると、127.0.0.1のみListenします。このままだとコンテナから/コンテナ間では突けないので、 手っ取り早く0.0.0.0 でListenする

続いてコンテナからのさし方は、Linuxの場合はホストのIPアドレスか、コンテナ内絡みたGatewayになります。Macの場合は192.168.65.2あたりになるはず、調べてください。
コンテナ間通信するなら、ssh-clientを名前解決で指せるのでここは楽勝。
1コンテナで攻めるならそもそも127.0.0.1のlistenで問題なし。

走らせ方とか

ソースコードの差し替えはバインドマウントでやります。 docker-compose使っていると思うので docker-compose exec <service> bash とかで入って泥臭くコマンド叩きます(いてます)。

AWSでの利用

AWSで使う場合は、いやGCEでも、もっと強烈な利用価値があります。感のいい人ならわかるかもしれないですが、この方法を使うとEC2ロールを今から走らせようとしているローカルのソースに踏襲できます。また、さらにprivate hosted zone は ECSのサービスディスカバリにも使われているので色々捗るはず。

Google Data Studio から AWS athena につなぐ

最近流行りのワンイシューってやつに見せかけてちょっと寄り道します。タイトルの通りですが、Data Studio の知名度や、GASについて色々気づいた点も触れます。実は応用の効く内容です。

f:id:iga-ninja:20190826010217p:plain

Google Data Studio ってなによ?

Googleが提供しているBIツールです。無料です。 正確には今は Google Data Portal というらしいですが、ググラビリティーが低いので、Data Studio で書きます。

メジャーなパブリッククラウドはたいていBIツールを持っていますが、無料ということも有りはっきり言ってかなりプアです。ただし、無料の割には結構やれるので、私の周りにもファンはいます。

中立的な評価とは言えないかもしれませんが、だいたい下記に書いてあることに賛同します。

https://www.holistics.io/blog/google-data-studio-pricing-and-in-depth-reviews/

BIツールを入れるとなったら、誰が編集可能で、誰が閲覧だけでとか、ユーザーレベルごとにライセンス料金が異なるとか、結構な面倒事がありますが、無料でそれこそGoogle Docsとかと同じ感覚で使えるので敷居は低いです。私はBI入門レベルなので、偉そうなことは言えませんが、これで感じをつかむのはいいかもしれません。

今回やること

  1. Data Studio のデータコネクタを自作する
  2. GASのソースをCLIで更新する
  3. Data Studio でAthenaのデータを参照する(ようは作ったコネクタを使う)

その前に種明かし
今回のネタはGitHubでたまたま見つけたものを使うだけです。他人の褌で遊ぶだけです。わかってらっしゃる人は下のリンクを辿って貰えばOKで、このエントリの内容を読むのは時間の無駄です。神に感謝しましょう!

github.com

データコネクタの自作

随分と敷居の高いことのように見えますが、結構かんたんです。 すごく丁寧に書いてくれているので、下記を読みましょう。

https://codelabs.developers.google.com/codelabs/community-connectors

GASの組み方(仕様、インターフェース)と、参照のしかたが書いてあります。

GASのソースをCLIで更新する

clasp という謹製ツールができています!やったぜ! 使い方はググってください。勘で使えるレベルだと思いますが、Web上のコードとローカルのコードが上書きされるだとかディレクトリの配置だとかは調べたほうがいいかも。ローカル上のファイル拡張子は.js で問題なし、このツールがPush時にgsに変換されます。

とりあえず手順だけ書きます。意味とかは自分で考えてください。

# とりあえず git clone とかで上記 datastudio-aws-athena-connector のコードを取ってくる
clasp login #初回ログイン
clasp create # standalone えらぶ エラーでコケたらメッセージ読む
vim .clasp.json

{"scriptId":"xxxxxxxxxxxxxx",
"rootDir": "src"}

clasp push
clasp deploy #出てくるIDぽいのメモっとく

Data Studio でAthenaのデータを参照

自作のコネクタを指すときに、clasp deploy の結果のIDを貼り付けます。 f:id:iga-ninja:20190826005906p:plain

あとはよくご存知のCredentialsとかを入れて、データ参照できれば完了 f:id:iga-ninja:20190826005929p:plain

脇道

aws.js(gs) について

今回の収穫の一番大きいのはこれです。できるのはわかっていたけど、こんなに小さくaws-api叩くのに最低限だけのセットを実装されていてこれはナイス。以前aws-sdk for javascriptを動かそうとしていたけど、XHRの実装がGASには無いので、これのラッパーかぁ、きついなと思って諦めた記憶がある。
まあ、そうだよね、これだけあれば動くよね。あとはテメエで aws api をググってなんとかしろやってことで。

clasp便利

これはいい!早いうちに決定版が出ないものかと思っていたけどようやくですな。helpとかも見てほしいけど、logs とか使えそう。
サーバーレスはデプロイ方法がまだ定まってないとずっとずっと言われているけど、少なくとも私には 大体こんな感じ で通用し、違和感なくコマンドが打てる。書いててただのポエム感もするが、世の中の大体こんな感じから大きく乖離していないと思うし、世の中の求めるものが少しずつ定まってきたと感じる。

data studio の進化

進化してると思います。出始めの印象はパブリッククラウド各社がBI出したんでとりあえずウチもってところだろぐらいでしたが、ちゃんと進化しててびっくりした。2テーブルをデータソースでくっつけたりとか昔はできなかった記憶。機能もバリバリではないものの、少しずつ追加されていて、有料版も見据えているのかもしれない。ちゃんとそだてる気、あるんだ。。と実感。

CLI向けhttpsキャプチャーを使う

全部httpsじゃないとだめな時代になりました。http通信の中身を見るだけならproxyで抜けばよかったですが、httpsの場合はそうも行きません。mitm(man-in-the-middle)をやって、httpsの暗号をproxyでほどかないといけません。
この手の情報がほしい人は限られていると思うので、細かい解説はすっ飛ばします。わかってらっしゃる方向け

f:id:iga-ninja:20190630015954p:plain

tl;dr

  • mitmproxyというcli向けproxyがある、cliなら多分これ一択
  • ca証明書指すのが面倒 がんばって調べて指す

元も子もないですが、もしあなたがhttps通信を行うプログラムの作者自体なら、log-level=debug,trace とかにしたときにヘッダを含めて全部標準出力などに垂れ流すようにしたほうが100倍楽です。そもそも世の中のhttp(s)クライアントはlog-levelを上げればverboseになるようにできてます。

対してこのmitmproxyは万能・汎用的である点は優れますが、ともかくca証明書がだるい。

mitmproxyの紹介

mitmproxy - an interactive HTTPS proxy

python製です。pipでも入ります。引数なしで起動したら 8080 listenして、 ~/.mitmproxy 配下にCA証明書とかを吐きます。あとは 環境変数 https_proxy と、このCA証明書を信頼してやればいいだけ。
CLIと書きましたが、mitmproxy自体はCLIですが、ローカルPCで動かして、GUIWebブラウザからプロキシ指してCA証明書をインポートすればブラウザ相手でも通信を抜けます。ブラウザ向けのやり方は公式を参照してください。
ブラウザ相手という視点なら、 macなら Charles, winなら Fiddlerとかとおんなじです。こいつらでCLIhttps通信を抜くことももちろんできます。が、Linux上でサクッとやりたいのでmitmproxyが良い。

他の競合はJMeterなどもこの機能があります、試験用のシナリオを楽に作成するときに使います。tcpdumpもサーバ側の秘密鍵が自分で手に入るなら復号化できるのでいけますが、外部サービスやらAPIの呼び出しはそんなの無理なんで、そういうケースを想定してます。

使い方は簡単です。 mitmproxy とコマンド打つだけ。あとは勘でいける。

各種ランタイムの証明書の通し方

こっからが本題です。ブラウザと違ってCLIhttps-clientは証明書の指し方がバラバラです。備忘録のように書き残し&書き足しておきます。

Java

javaはproxy自体の指し方がちょっと特殊です。
-Dhttp.proxyHost=127.0.0.1 -Dhttp.proxyPort=8080
と、オプションを渡せるならそれで、無理なら環境変数 _JAVA_OPTIONS に足してやればOK

問題は証明書です keytool という java 用のコマンドを使って追加します。

sudo $JAVA_HOME/bin/keytool -import -trustcacerts  \
-file /path/to/mitmproxy-ca-cert.pem                 \
-alias mitmproxycert                                 \
-keystore $JAVA_HOME/jre/lib/security/cacerts

$JAVA_HOME/jre/lib/security/cacerts 以外の場所に作るなら java実行時に java vm のオプションである -D<hoge>で指定ができるはず。 $JAVA_HOME/jre/lib/security/cacerts には パスフレーズがついていて、デフォが changeitらしい。

blog.packagecloud.io

のパクリ

ruby

私の経験値が低いため、イレギュラーを知らないせいか比較的散らかってないと思う。 proxy自体は https_proxy環境変数でほとんど通るはず。 証明書は ruby -ropenssl -e "p OpenSSL::X509::DEFAULT_CERT_FILE" で調べて、そこに追記しちゃうのが多分てっとり早い。大体システムのデフォルトだと思う。

python

証明書はイレギュラーだらけでやばい、proxy自体は環境変数でまあいける。 package次第だが、たしか requests とかは独自で ca-certを持っていて、バージョン次第では証明書だけで別のpackageになっている。

dev.classmethod.jp

使うライブラリ次第で使っている証明書も異なる。頑張って調べてねとしか言えない。

nodejs

proxy自体は環境変数でOK。 証明書はある意味一番特殊で。システムのca-certは一切参照しておらず。なんとハードコードで nodeのバイナリ本体に内包されている。システムの証明書を使う場合は

--use-bundled-ca, --use-openssl-ca

が使え、今回のような専用の証明書を指すなら

NODE_EXTRA_CA_CERTS=file

https://nodejs.org/api/cli.html#cli_node_extra_ca_certs_file

という環境変数で行けるらしいです。対応バージョンは v7.3.0からとなっていますが、旧バージョンにもLTSならばもある程度バックポートされているらしい(未確認)

まとめ

このライブラリがどんな動きしてるかわからない?となったとき

  • ログレベルを上げて実行する (-v とか -l debug とか DEBUG=1 とか)
  • それでトレースできないなら、プログラム意地れるなら loggerにだすなり echoするなりする
  • それでもだめなら、無理なら サーバの秘密鍵取れるなら tcpdumpとか
  • それも無理なら mitmする

見た目はいいんですよ。機能も多機能。だけど使えるようになるまでが面倒。

EC2 DNS名前解決制限をECSでも回避する方法

非常にググりにくい事柄なんですが、なるべく浮世離れしないタイトルとして書きます。

f:id:iga-ninja:20190415012916j:plain

EC2(正確にはENI)からの名前解決、つまりリゾルバとしてのOutboundには、全て制限がかかっています。1024パケット/秒です。

VPC での DNS の使用 - Amazon Virtual Private Cloud

ECS DNS というキーワードで探すと、ほぼ間違いなくService Discoveryがうじゃうじゃ出てきますが、これはリゾルバ・キャッシュサーバの話です。 これをEC2で回避する方法はAWSが書いてくれています。

Avoid DNS Resolution Failures with EC2 Linux

似たようなことをECSで、なるべくエレガントにやります。わざわざこう書くのは、ちょっと泥臭いという意味です。

どういうとき問題になるか

そもそもこのポストを見ているひとは、今現に困っているはず。ですが意識合わせ。

  • LinuxDNSローカルキャッシュしていない
  • 1台のEC2でかなりのトラフィックをさばいている
  • 現に名前解決しましたとかエラーが出てる
  • 負荷試験とかやってて、性能が頭打ちしてる

ECS & EC2で、詰め込むコンテナの数もかなり多い場合に起こりやすい現象です。

どうすればよいか

上記のAWSのドキュメントを読めばわかりますが、ローカルキャッシュを持つようにして、とにかくEC2(ENI)からOutboud:53 を抑制するしか手はありません。
VPC内のリゾルバに対する問い合わせの回数が問題ではないのです、1歩でも53番ポートへ出ようとする通信全てが対象なので、自前でリゾルバを1台用意しても無駄です。
コンテナの場合は、コンテナ内部からの名前解決をケアすることになります。つまりはコンテナ内の /etc/resolv.conf を何らかの方法で書き換える必要があります。

  • どうやって変えるのか
  • 何(IPアドレス)に変えるのか

これが主題。

最終構成

DNSキャッシュ機能

まずは何らかの方法で、DNSキャッシュサーバをコンテナインスタンス内に立てます。他にも選択肢がありそうですが、メジャーどころで2択

  • unbound
  • dnsmasq

どちらを選ぶかは好きな方で良いのですが、dnsmasqのほうがおそらくフットプリントが小さいし、必要なことにマッチしているので良いと思います。ただし訳があってunboundにも触れます。
選んだキャッシュサーバを各EC2に一つづつ立ち上げます。お好きな方法で良いです、EC2に直接インストールするか、ECSならDAEMONモードのServiceとして、一家に一台を実現するか、ここではDAEMONで動かします。

コンテナからの参照

各EC2に立てるDNSキャッシュサーバーをプライマリ、VPCごとについてるリゾルバをセカンダリにします。
コンテナの中のresolv.conf をいじれば良いとなります。ホストのではなく。

コンテナ内のresolv.conf

ECS & EC2なら、(おそらくほとんどのケースで)ホストのresolv.confがコピーされます。
仮にホストで直接キャッシュサーバーを動かしたとして、

と書いた場合ホスト自身は名前解決問題なしですが、コンテナはから見ると127.0.0.1 はコンテナ自分自身のloを指すことになり、当然 port:53 でサービスしていないのでNGです、上記例の設定ならは、コンテナ内からの名前解決は、必ず1手目でタイムアウトしてセカンダリであるvpcのリゾルバに行くので、この設定は意味なしです。

じゃあ、ホストのローカルIPを挿せばうまく行くのかと言ったらうまく行かないです。やればわかりますが、ホストのローカルIPで問い合わせたが、レスポンスを返したIPが違うって怒られます。

なので、コンテナから見たゲートウェイ (bridgeの場合) 172.17.0.1 固定を指します。

IP固定してOKなん?

他所では場合によっては固定じゃないっていっぱい書いてありますが、ここでは大丈夫です。
説明すると長いので、断言できることだけ。少なくともECSのブリッジモードは、bridgeという名前のついたデフォルトのdockerネットワークにすべてのコンテナが入ります。よって、Gateway172.17.0.1 で確定です。Docker自体の仕様がかわったり、ECSのブリッジモードでネットワーク名をユーザーが新設できるようになるとかならない限り、ここは崩れません。
当たり前ですが、リゾルバにホスト名は書けないので、同タスク内のコンテナ同士の名前解決をLink等でできるようにしてもIPを書かねばならないので意味ないです。

ECS/EC2 での実装

キャッシュサーバ(コンテナ)の構築

サーバというものの、全EC2に立つのでそのつもりで。とりあえずecs-cliで書いてみました。 書き忘れましたが、キャッシュサーバ(コンテナ)と、メインのアプリコンテナは別タスクとなります。
docker-compose.yml

version: '3'

services:
  dnsmasq:
    image: himaoka/dnsmasq:latest
    ports:
      - 53:53/tcp
      - 53:53/udp
    cap_add:
      - NET_ADMIN

Dockerイメージの中身はGithubで見てください。注目は cap_add: NET_ADMIN です。コイツがないとコケます。でこれがあるからFargateやawsvpcモードでは使えません。更に補足としてこの cap_add はManagementConsleからは見えません(JSONベタ書きはいけるかもしれない)。ECS-CLIですら機能のすべてを網羅していませんが、タスク定義作成にブラウザポチポチはすぐに卒業したほうがいい、terraform や aws-cli などをすぐに使えるようにしてください。

このコンテナにより、コンテナインスタンスの port:53 でDNSリクエストに応答します。

ecs-params.yml

version: 1
task_definition:
  services:
    dnsmasq:
      mem_limit: 128M
      mem_reservation: 128M

多分こんなにメモリはいらない。 これで定義したタスク定義を、サービスとして設定、DAEMONで動かします。
まあ、ここでコンテナにこだわる理由はないです。よくわからんならEC2で素で動かしても全く問題なし。

アプリコンテナ

とりあえず何でもいいんですが、手っ取り早く動かして、docker exec で動いているコンテナに入り込んで確認します。なんでただ死なないコンテナであれば何でもOK

docker-compose.yml

version: '3'

services:
  nginx:
    image: nginx
    dns:
      - 172.17.0.1
      - <VPCのリゾルバのIP>

この dns はこれまた awsvpcモードでは使えません 理由はわかりません。できても良さそうなもんだけど。こっちはブラウザポチポチでも一応設定可能です。
確認方法は exec で入り込んで dig コマンドとか突っ込んで確認してください。

もう一つの方法・コンテナインスタンス側が譲歩する

基本コンテナインスタンスの resolv.confをコピーするという動きがるので、コンテナインスタンス内部側のリクエストは犠牲にして、コンテナ内部のリクエストだけをちゃんとしてやることもできます。単純にコンテナインスタンスの /etc/resolv.conf のプライマリを 172.17.0.1 にするだけです。
コンテナインスタンスからの名前解決はプライマリはすべて失敗、セカンダリとしてVPCのリゾルバを挿せば、常に1手損ですが通ることは通ります。
タスク定義を直さないでインフラの豪腕でなぎ倒すことは一応可能です。エレガント?な方法は下記です。

Amazon EC2 Static DNS Ubuntu Debian

Fargateならどうすんの?

まず、必要となるか否かですが、なんとも言えないです。Fargateということは一つのタスク全部で1024という制限になるので、上限に引っかかる可能性は減ります。
じゃあ、絶対に大丈夫か?と言われると場合によるかもしれない。1タスクに10コンテナとかあり、それらが各々がガンガン名前を引くならば絶対にOKとは言い切れない。

と私は思うので、一応試しました。ただし結論から書くと、きれいな方法は取れないです。

  • EC2という概念がなくなるので、メインアプリのコンテナと、DNSキャッシュコンテナは同タスクとする必要がある
  • DNSキャッシュサーバのListenは、メインアプリのコンテナからみても 127.0.0.1:53 となるので、nameserverは 127.0.0.1 とすればよい

対して阻害要因です

  • dnsmasqのcap_add: NET_ADMINはFargateでは使えない
  • メインアプリコンテナのDNSとして、タスク定義としてdnsを指定することはできない (awsvpcモードの制約)

Fargateの結論、一応できたけど、、

dnsmasqのcap_addはどうにもならないので、unboundにします。
dnsも無理なんで、Dockerの ENTRYPOINTで/env/resolv.confを無理やり書き直します。
で、一応動きました。いい方法とは言えないので、解説はしませんがこの概要だけ書いて終わります。

unboundの注意点

unboundでやるときの注意点ですが、dnsmasqとこちらは違って、ルートDNSへのヒントを持っているので、権威サーバのレコードであれば自力のみで解決できます。対してdnsmasqはキャッシュはするものの、自力で権威サーバを調べ上げることはできない。
じゃあ、なんの問題もなくね?
いやいや、 問題大有りですわ。何も設定してないと、Route53の Private Hosted Zoneのレコードが全部死にます。

先に答えを書くと、unboundで行くならば、必ず すべてのレコードをVPCのリゾルバにForwardしてください。

厄介な Private Hosted Zone

個人的には嫌いな機能です。Private
Privateは作るときに必ずアサインするVPCを指定しますが、これは VPCのリゾルバに権威サーバに飛ばさずに Private Hosted Zone に横流しするルートを作ります。もちろん特定のレコードだけですが、これがあるのですべてのレコードをVPCのリゾルバにForwardするのがインフラ屋としての正しい解です。
Privateのレコードがこれ以上増えないってわかっているならば、個別ドメインに対してのForward設定してもいいです、がECSやってるならService Discoveryの重要性もわかっているはずです。でもってService Discovery の実態は Private Hosted Zone です。特定ゾーンだけForwardしてると、Service Disvoveryをある日いきなり使い始めたら終わりです。

おまけ unboundで起動時警告が出る

cap_add: SYS_RESOURCE でいいはず、もしくはulimitをタスク定義側で指定。  

おまけ2 ubuntu の場合

あんまり調べてないですが、ubuntu ユーザーなんで。ubuntu はデフォルトで 127.0.0.53:53 でListenしています。ホストの resolv.conf も 127.0.0.53 を参照していますので、そのまんまやるとホストの resolv.conf をコピーして不味そうですが、うまく行っています(書き換わる)。理由は調べてないですが、昔はちょっと問題があったらしい。今回のテストをローカルPCでやるときにちょっと手こずった。