続 カッコの付け方

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

terraform で gcp iam管理に入門してみる

terraform で gcpのIAMを管理してみたときのメモ

f:id:iga-ninja:20210214015531p:plain
terraform with gcp

GCP/AWS 用語対応表

AWSからterraformを触った人が多数派と思うので、用語の意味違いが結構な落とし穴です。なので整理しときます。

AWS GCP memo
User Google アカウント GCPより広い世界
Group Google Group 同上
Role ServiceAccount 厳密には異なるがだいたい
Policy Role つらい、嫌がらせか?
PolicyAttachment IAM_member terraform上
該当なし? Policy terraform上?

まずUserとGroupについて、GCPのIAMは認証機能は持たず、基本的に認可だけやります。認証は、GCPに閉じた世界ではなく、Gooleアカウント/Groupで行っています。Gmailのフリーアカウントや、Google Workspaceのメアドがユーザーに該当します。

次にAWSのRoleに対応するものは、ServiceAccount(SA)というものです。GCPにもRoleがありますが、これはAWSのPolicyに相当します。まあ、英語の意味はあってますが、後発なので敢えて変えたんでしょうね。。AWSのRoleは主に人間以外のサーバやSAMLで使うものですが、GCPで同じことやる場合、人間以外のためにメールアドレス(メールは受信できないが)を払い出します、それがServiceAccountと見ていい。

AWSのPolicy=GCPのRoleは認可内容のセット・塊です。GCSの全権限を持つとか、GCE何でもできる権限とかを指します。AWSのIAMのようにどのサービスの何ができるということは個別で指定できます、が最初のうちは、プリセットのRoleが用意されているのでこれでほぼことが足りるはずです。プリセットのRole=AWSのAWSManagedのPolicyです。GCPのRoleは自作もできます。

注意喚起です、google_project_iam_policy というAWS触った人ならすぐにいじりたくなるものがありますが、基本いじっちゃだめです。本番でやると即死あり。

Terraform x GCP で、IAM権限を全削除してしまった - Qiita

https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam#google_project_iam_policy

最後にPolicyAttachment、これは厳密にはAWSではなく、terraformの話ですが、どのAWS User/RoleがどのPolicyと紐付いているかをつなぐものです。これがGCPでは google_project_iam_member に該当します。

terraformでの認可の振り方

2つやり方があります。google_project_iam_bindinggoogle_project_iam_member です。結論を先に書きますが、AWSのterraform猛者であれば google_project_iam_member 一択です。 google_project_iam_bindingをどうしても使いたい場合であっても、人間ユーザーが使うものだけに適応するのが望ましく、ServiceAccountで使うのは悪手です。その説明を書く。

google_project_iam_binding

Roleを主体にして、それを使う人やServiceAccountを列挙します。だから members = [...] で指定します。AWSでterraformそこそこ昔からやってた人ならすぐにわかると思いますが、これを使ってる=死亡フラグです。

google_project_iam_member

Member=使う人やServiceAccountとRoleを1対1で紐づけます。aws provider (terraform) の IamPolicyAttachmentです。1対1紐づけの重要さを知っている人はこれから先は読まなくていいです。お疲れ様でした。

1対1紐づけでないと何がまずいのか?

terraformの習熟度次第では「そんなの俺には関係ないね」となるかもしれないですが、知ってて損はないと思うのでまあ、読んでください。 私が認識するterraformの良さの一つに、コードで管理する対象(バケツやGCE-VM GCPのリソースのこと)を選べる、を挙げます。例えば同じGCPプロジェクトでも、このチームはVPCだけ、このチームはGCSだけとかを、それぞれ異なるtfstate(実態としてはディレクトリとなる)で管理できる点です。
「インフラなので、1枚岩で1プロジェクト1ディレクトリで全部のリソースを管理しないとダメ」という管理ポリシーももちろん間違えではないですが、ユーザーが選べるという点が優れてる、嫌なら分割しなきゃいい。
で、google_project_iam_bindingを使ってしまうと、この管理対象を実質的に分割することができなくなってしまいます。(できなくはない、が運用が大変)まあ、ディレクトリ分割しなくてもtfファイルはかなり不格好になります。
もう一つは terraform workspaceを使っている場合も(知ってると思うけど)危ないです。回避方法は google_project_iam_member を使えです。

実証1 (神レビュアがいれば避けられるかも?)

四の五の言わずにさくっとtffileを書きます。まずは分割しないけどapply後に延々とdiffが出るパターンです

resource "google_service_account" "sa-one" {
  account_id = "sa-one"
  display_name = "sa-one"
  description = "テスト用1"
}

resource "google_project_iam_binding" "sa-one" {
  role = "roles/storage.hmacKeyAdmin"
  members  = ["serviceAccount:${google_service_account.sa-one.email}"]
}

### 1ヶ月後に誰かがsa2を追加
resource "google_service_account" "sa-two" {
  account_id = "sa-two"
  display_name = "sa-two"
  description = "テスト用2"
}

resource "google_project_iam_binding" "sa-two" {
  role = "roles/storage.hmacKeyAdmin"
  members  = ["serviceAccount:${google_service_account.sa-two.email}"]
}

Roleは間違って流してもいいように、普段使わんであろうやつを指してます。sa-one にこのロールを当ててその後、sa-twoにもそのロールを当てようとしてます。これは plan/applyともに通ります。しかしapply後に planを叩くと差分が出ます。というか何回applyしようが永遠に出ます。なぜか?
google_project_iam_bindingの書き方が間違っています。members = [] で2つのSAを列挙しないとだめです。正しい使い方は、Roleを主体で考えるので、それを使っている人・SAを全部列挙しろということになります。これ、とても恐ろしいことが起こってます、仮に人アカウントのRole割当は一切terraform管轄にしていない場合に、上記のRoleがProject/Editorだったりした場合、Applyしたら人アカウントからEditor権限全部剥奪されます。そのため、例ではどうでもいいRoleを採用してます。

terraformコード管理してます、Reviewを通さないとApplyできないという運用をしてます、sa-oneを参考にしてsa-twoをベースコピペでPR出しました、Planもグリーンです、さあレビューしてください!私は急いでます!、これでレビューでNGだせますか? シンプルに google_project_iam_bindinggrepして引っかかったら「これあかんで」ぐらいの運用しかできないんじゃないかな。

今回は、1ディレクトリに2つのSAを両方書きましたが、ディレクトリ分割し、tfstate分離してたら悲惨です。他のディレクトリのSAで、Roleの参照があるかないかを調べて忖度し続けなければならない。

実証2 (workspaceパターン)

workspaceでも試してみた、こちらはworkspace切り替え後のapplyで差分が出るので気づけるチャンスはある

resource "google_service_account" "sa-one" {
  account_id = "sa-one-${terraform.workspace}"
  display_name = "sa-one"
  description = "テスト用1"
}

resource "google_project_iam_binding" "sa-one" {
  role = "roles/storage.hmacKeyAdmin"
  members  = ["serviceAccount:${google_service_account.sa-one.email}"]
}

defaultでapplyしたあと、別workspace(production)でapplyすると、差分が出ます。

Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # google_project_iam_binding.sa-one will be updated in-place
  ~ resource "google_project_iam_binding" "sa-one" {
        id      = "pj/roles/storage.hmacKeyAdmin"
      ~ members = [
          - "serviceAccount:sa-one-default@pj.iam.gserviceaccount.com",
          + "serviceAccount:sa-one-production@pj.gserviceaccount.com",
        ]
        # (3 unchanged attributes hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

差分が出る原理は同じですが、ちゃんと想定外のが出ている分だけましか。。

まとめ

  • GCPでも人・SAとRoleの紐づけは1対1 google_project_iam_member を推奨します
  • google_project_iam_binding をどうしても使いたい場合は、(ちょっと苦しいと思うが) SAに割り当てるRoleは全部スタムで、SAにしか割り当てない運用とする

エンジニアなので、コード短く書きたい、DRYに書きたい、変数でループしたい、よくわかります。しかしIAM設定はミスったときのダメージが大きすぎるので、リスクおよびその回避策とを天秤にかけて選択してください。

localstack有料版をちゃんと使ってみる

数ヶ月前にカッとなって課金だけしてたlocalstack Pro版を触ってみたのでその記録

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

localstackのPro(有償)版とは?

localstackはaws-apiをローカル環境で再現するものです。それの有償版がPro。できること・できないことは
https://localstack.cloud/ を見てください。

だけど これだけ見てもよくわからんよね? なので自腹を切って試してみた、というより面白そうだとおもったから、とりあえず課金した、英語を含めてもあんまり有償版のポストが見つからなかったため。

システム的な違い/機能Activateなどの話

Communitiy版をdocker-imageで無償で公開してるので、Pro版はどう違うんや?と思ってたけど、Pro版の場合は、API_KEYを環境変数として仕込むことによって、認証サーバとおしゃべりして機能を有効・無効化しているはず。
なので本当に特定サイト以外インターネット全面禁止な社内とかからだと使えないかもしれない。14日のトライアルあるからそこで試してね。

あとは外部URL=dockerではないWebの画面が付きます。Community版でもあるらしいですが、いつまで続くかわからないらしい。便利かと言われると、ちょっと微妙、Readに特化しているので、AWSのManagedConsoleみたいにポチポチでリソース作ったりできない。あるだけマシかな程度です。

始めるぞ

とりあえず使ってみる。ネットにある古い情報を整理する意味でも書いているので、Communitiy版でも有用なこと書いてます。

APIキーを取得する

とりあえず課金する&サインインする。
次にAPIキーを取得する https://app.localstack.cloud/account/subscriptions から

localstackコマンドはいらない

https://app.localstack.cloud/docs#installation

にそって進めれば良いですが、いろいろ情報が古いんで補足しながら。 まず
pip install localstack と書いてありますが、無視します このコマンドなにができるかというと、docker-composeとかではなく dockerコマンドをラップしているコマンドなんですが、私の環境だと後述する ローカルDNSによる透過的なアクセス のために privileged で動かそうとするので、Linuxでuser-namespace使っているユーザー(開発者はほとんど使っている認識)と相性が悪い(というか動かない)です。「俺はLinuxなんて使う変態じゃないんや!」という人が大半だと思いますが、このローカルDNSによる透過的なアクセス、私の目から見て無理筋です。ほとんどの人がこの機能の恩恵を受けられない。詳しくは後述します。

docker-composeを書いて upする

localstackコマンドはほとんどの人にとって役に立たないと思うので、docker-compose書きます。Pro版との違いは 環境変数LOCALSTACK_API_KEYの有無と443のexposeです。ここで古い知識のUpdate、昔のlocalstackはサービスごとにListenポートを分離していましたが、最近それがなくなりました。4566で全サービス受けます。
https://app.localstack.cloud/docs#installation

の記載もUpdateされてないので、github上の最新をもとにこんな感じに

version: '2.1'

services:
  localstack:
    container_name: "${LOCALSTACK_DOCKER_NAME-localstack_main}"
    image: localstack/localstack
    network_mode: bridge
    ports:
      - "443:443"
      - "4566:4566"
      - "4571:4571"
      - "${PORT_WEB_UI-8080}:${PORT_WEB_UI-8080}"
    environment:
      - LOCALSTACK_API_KEY=[your-api-key]
      - SERVICES=serverless,cognito,rds,athena
      - DEBUG=${DEBUG- }
      - DATA_DIR=${DATA_DIR- }
      - PORT_WEB_UI=${PORT_WEB_UI- }
      - LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR- }
      - KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }
      - DOCKER_HOST=unix:///var/run/docker.sock
      - HOST_TMP_FOLDER=${TMPDIR}
    volumes:
      - "${TMPDIR:-/tmp/localstack}:/tmp/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"

あとは docker-compose up -d

awslocalコマンドいれる

awslocalコマンドは aws-cli で --endpoint=http://localhost:4566 とか打つのをかぶせたawscliです。
https://github.com/localstack/awscli-local

四の五の言わずにいれとけ、便利だから。補完が効かないので最初困りましたが、zshの人は

complete -C '/usr/local/bin/aws_completer' awslocal

確認してみる

ネットに転がってるやつで

awslocal s3 mb s3://test

この状態で https://app.localstack.cloud/resources/s3 みると

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

機能拡張を見ていく

全部はみてません。この機能のテストをやりたいって考えている人は、そもそもAWS識者のはずなのでクドい説明は無しでいきます。

大前提、ポチポチではリソース作れない & リソースはすべてlocalマシン(Pro版でも)

最初に繰り返しですが、ポチポチ(=Webブラウザ操作)でリソースは作れません。ので全部API操作です。terraformとかは頑張れば使えるかもしれないですがやってないです。
そういう目的で使うならば、CloudFormationは対応しているようです、がlocalstack用のテンプレートがそのまま本番で使えるかはもの次第でしょう。今思い出しましたがこれはserverless-frameworkのためですね。

さらに一応localstackという名前がついている通り、全てのリソースはlocalマシン上に格納されます。そのため本物のAWSのようなそこそこ巨大なりソースを複数個並べるとか無理です。なので、インフラのテストとして使うのはミニマムな構成を除いて今の所ちょっと守備範囲狭い。

RDS

どのように動くのか興味ありました

  • API部分は一応全部完備(ほとんどmockだけど)
  • DBエンジンとしては postgresがlocal-docker内で動く
  • RDS data APIが動く

のようです。

なんとMySQL非対応、そのうち対応するらしい。データストアとかを app.localstack.cloud 側で保存してくれるわけでもない。 メリットは RDS data APIが動くので、serverlessなLambdaから突くのにはいいかもしれない(よくできましたポイント)。

ElastiCache

  • API自体はRDSと同じ
  • Memcacheは非対応

普通に redisをdockerで上げればよくね?

ECS & ECR

ECR は ECS/EKS使うなら多分セットなので対応できているのよい、というかできてないと話にならない。
ECSも一応対応しているようですが、ドキュメントにもALBとかが絡む複雑な構成は無理って書いてある。
ServiceDiscoveryとかもまあ無理でしょ、対応できてもだいぶ未来になりそう。
現状だとあんまりテストにならないな、素直にAWS使ったほうが早いといった印象。

EKSはご想像どおりです。ECSよりもっと複雑なんでまともに使えるようになる未来はまだまだ先。

Athena

余力があったら実際に試すまでやってみようと思ってる、が正直微妙。Docから抜粋

Note: In order to use the Athena API, some additional dependencies have to be fetched from the network, including a Docker image of apprx. 1.2GB which includes Presto, Hive and other tools. These dependencies are automatically fetched when you start up the service, so please make sure you're on a decent internet connection when pulling the dependencies for the first time.

結局LocalでPresto回してるのと同じなんで、S3にクエリがかけられたとしてもGlueカタログとはまた違う代物なので、完全にAthenaとは同じにならない。
それとGlueクローラがないのでデータを用意してスキーマを更新してとかをSQLベースでやらなきゃならないので、そんな暇あるなら本物のAWSでやればええとなる。
まだ試してないのでわからないが、GlueカタログがないAthenaが動いてもほとんど意味ない感はする。EMRの、PrestoやるついでにReadyとしたんかな。

IAM Security Enforcement

良くできましたなポイントです。 2020/10/25時点では正直なところ微妙な機能。使えばすぐわかりますが、これを有効にするとDynamoDBが使えなくなる(Deniedとログに出る、つまり仕様)ので存在意義が見当たらない。

認証だけでなく・認可(Policy)が通ります。 サンプルとしては Credentialだけを発行して、 s3 mb 叩いてNG policyをちゃんとつけて mb成功させてます。リソース縛りとかに対応しているか、他のサービスもちゃんと動くのかは試してませんが、これがちゃんと実装できてれば素直にすごい。

ただ、経験上わかっていることとして、IAM policyはとてつもなく複雑なので、localstackで検証してOKなものがAWSでも同じように動くかは疑問です。完全実装はちょっと無理じゃないかな労力的に。EC2はないもののLambdaはRoleが必須なのでちょっと試してみる。

IAM policyどうやってるの?

ちょっと調べてみた。実装上はmotoから借りているものが多く、motoも一応認証・認可の挙動ができるらしい。
https://github.com/spulec/moto/blob/master/README.md#iam-like-access-control

どちらの実装も読んでみたけど、リソース縛りとか、S3以外のサービスでは動作しなさそう。ToDoにも書かれていたが、リソース絞りとか完璧にやろうとすると、作製済みリソースをどこかに保持しないといけないので、単なるmockの範疇を大きく超えることになる。localstackは単なるmockではないので拡張実装としてどこまでやれるかは見ものだが、これは大変な作業になりそう。

Configuring Local DNS Server

前半に書いた透過的なローカルDNSアクセスです。これは無理筋です。
どういうふうにやるかというと、経典みたいに本ブログに何度も出てきているMITM的なやり方です。 amazonaws.com の名前解決を横取りして、localstackに流すことによって --entrypoint=localhost...の指定を無しにする魂胆。DNSを捻じ曲げてhttps通信したら何が起こるか。「そうだね。証明書エラーだね。」

Note: When you configure transparent execution mode, you may still have to configure your application's AWS SDK to accept self-signed certificates. This is a technical limitation caused by the SSL certificate validation mechanism, due to the fact that we are repointing AWS domain names (e.g., *.amazonaws.com) to localhost. For example, the following command will fail with an SSL error:

これで transparent execution mode って言えるんかね?

CloudFormation Support & Serverless Integrationのextended

何がextendedなのかまだ調べられてない。今私は結構serverlessから離れてしまっているので、やってみます。serverless(sls) が CloudFormation必須なの忘れてた。

感想

  • serverless開発のローカル環境としては良さそうだが
    • Communitiy版と比較して明確なプラスは Cognito & API Gateway V2かな
  • インフラエンジニアがインフラのテスト(コード上)をやるための実験場としてはまだまだ無理
    • そんなやつおらへんと思うけど、terraform apply を localstack にぶち当てることができてもほとんど mockなので幸せにはならん
  • (ビック)データ系のサービスは対応とはあるものの、本物のAWSとの乖離が大きすぎて使い物にならない
    • 練習台・入門用としても逆に難しくてだめ

役に立つかは人それぞれ、14日のトライアルでちゃんと使って見極めてください。

EC2 roleや ECS task role の知識をUpdateする (IMDSv2とかも)

まず最初に、本当にやりたかったことと、調べているうちにだいぶ脱線して得た知識を書いてます。少々規模がでかいです。

最初にやりたかったのは、 EC2 role (Instance Profile) を使っているときに awscliとかで Unable to locate credentials が稀に出るという問題の解消です。
で、いきなり(今わかっている) 答えを書くと、

  • AWS_METADATA_SERVICE_TIMEOUT
  • AWS_METADATA_SERVICE_NUM_ATTEMPTS

あたりの環境変数を指定しておくです。
AWS CLI Configuration Variables — AWS CLI 1.18.123 Command Reference

上記を含む、大部分が脱線した内容を掘り下げて行きますが、前提となる知識が結構多く、いちいち細かく書くと長すぎるので、AWS上級者向けです。

EC2ロール

EC2ロールってどうやって動いている?

そもそも私も理解が間違っていたのでここから始める。なぜEC2ロールを利用するのかは前提知識なので説明省くが、EC2を起動するときに、EC2ロール/InstanceProfileを指定したらなんでその権限が使えるのかを調べる。

これはググればすぐに出てくるが、AWSのドキュメントにもある。169.254.169.254でお馴染みのインスタンスメタデータに credentialが格納され、それをbotocoreとかaws-sdk/ライブラリが参照している。まず最初に私が間違っていたのは、メタデータにいきなりcredentialが入っていることはないと思っており、てっきりaws-sdkとかがstsを叩いて発行していると思っていたが、そうではなくていきなり使える状態でメタデータから取得できる。もちろんtoken付きなので有効期限はあるが、自動で更新された状態でメタデータに入ってくるらしい。Expireの5分前には新しいのに更新されているらしい。

早速自分の誤解が一つ減った。
VPC-Endpointを使い、インターネット辞めたいとき、stsへのエンドポイントがなくともEC2ロールは使える。
本当にstsのエンドポイントが必須なのはEC2でさらにassume-roleしたい場合のみ。

EC2ロールを使ってるときになんでUnable to locate credentials

おそらくメタデータの取得が失敗することがあるのではないかと予想している。つまり curl 169.254.169.254 が稀に失敗している?。それを見越してのAWS_METADATA_SERVICE_NUM_ATTEMPTSなのかなと思う。ただしこの実装は botocore など aws-sdk側の実装で実現されている。よって、sdkなんか使わない独自実装はともかく、場合によってはsdkの言語やバージョン次第では対応していないかもしれない。ここはちょっと確証が持てないので、いまも実験中、そもそもどれぐらいの頻度でエラーになるのか。

IMDSv2?

メタデータとさっきまで書いていたやつのv2版のことです,ということはいままではv1だった。v2が出た理由は一応はセキュリティー観点かららしい。ググればたくさんでてくるが

  • 問題点であるSSRFは、一発即死系のかなりやばいやつ
  • v1にくらべてv2はちょっと攻撃をやりづらくはするが、完璧では全然ない

詳細は下記で詳しく述べられています。AWSに限らずクラウド屋なら必読です・他のプラットフォームでも同じ問題があります。

SSRF対策としてAmazonから発表されたIMDSv2の効果と限界 | 徳丸浩の日記

このエントリでもIMDSv2関連を書きますが、SSRF対策は真剣に取り組むべきです。とりあえずすぐに対応できそうなことを一つ上げておくと、EC2ロールだからといって、無駄に強い権限を割り当ててはいけない

IMDSv2 / v1 の違い

違いを列挙

  • v2/v1ともにIPはおなじ、v2はHeaderが必須になる
  • v2は最初にPUTをうってTokenをもらい、そいつを使う、v1はTokenとかなしにいきなりデータ取れる
  • デフォルトで v2 / v1 両方が生きた状態で起動。実質セキュアにするならv1を殺す必要あり
  • IMDSを設定するためのmodify-instance-metadata-options
  • PUTはデフォルトでhop=1でしかたたけない、つまりdocker・コンテナ内からはデフォルトたたけない

今回ハマったのは最後のやつ、network的に、ルーターかます(forwardする)とあかん。ので、hostモードでdocker内からたたけない。

aws-sdkとかの credential取得方法

結構なところで言及されているが、環境変数・所定の設定ファイル・EC2ロールなど、優先順位別に試していき、見つかったものを使う。これはみんな知っているはず。

aws-sdkとかの IMDSv2 対応

上記より、EC2ロールが実際に評価される場合、どうやってとっているのか?botocoreで実装されているのでちょっと読んで調べた。

  • v2のためにPUTをいきなり打つ(Token取得)
  • Token取れなかったら v1方式で、取れたらv2方式
  • 最初からv1でやれ!という設定は今の所できない

言語によっては aws-sdkで IMDSv2が出たしたときにちょっと問題になったらしいが、今の所上記の通り。

さあ、感のいい人ならもう気づいたと思うが、コンテナ内からEC2ロールを使おうとしたときどうなるか。
答えは最初のv2のPUTがhopの問題で失敗し、デフォルト設定だとcredentialの取得が常に1秒遅くなります。
さらに冒頭で書いた AWS_METADATA_SERVICE_TIMEOUTを30とかに弄っている場合はどうなるか?常に30秒遅くなります。

いやいや、そもそもコンテナからEC2ロールつかうなよ・Taskロールつかえよ・きょうびFargateやろ普通

正論。

だけど俺の経験としてTaskロール使えないケースがちょいちょいあるんだよな。EC2ロール・Taskロールが使えるのは、aws-sdkのバージョンや別にSDK使わず自前実装しているならば、アプリ側の対応次第となる。特にどうしても古いアプリを使わなければならないときなので、ベースとなっているaws-sdkが古すぎて Taskロールガン無視・EC2ロールは使えたりとか結構ある。OSSなら新しいsdkを指すなり、独自実装ならPR出すなりすればいいと思うけど。

今回の問題の対策

AWS_METADATA_SERVICE_TIMEOUTのデフォ1秒を30秒とかにしたときにEC2ロールからcredentialの取得が常に遅い
の対応は modify-instance-metadata-options--http-put-response-hop-limit 2 を指定しろになる。

そもそもコンテナ内からEC2ロール使わなくて済むならこんな対処いらないし、Taskロール使うべき。

Taskロールについて

話がそれるが、ついでなのでTaskロールの実態について調べてみた。

取得方法

IAM Roles for Tasks - Amazon Elastic Container Service

curl 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI

よく見るとIPアドレスが違います。170.2です。のでIMDSではない。次に $AWS_CONTAINER_CREDENTIALS_RELATIVE_URI 環境変数ですが、ECSでTaskロール指定して稼働しているとついているらしい。実際にそのようになってます。で、Taskロールが使える状態で、curlで IMDS(=つまりEC2ロール)もみえるかcurlでやってみた。すると「普通に見えたぞ」。

ちゃんとコード読んでないけど、状況証拠からaws-sdk

  • $AWS_CONTAINER_CREDENTIALS_RELATIVE_URIの有無をみてTaskロールがついてるか判断
  • Taskロールついてる判断したら 169.254.170.2のほうで取得する
  • Taskロールついてない判断したらEC2ロール試す

となっているはず。つまり、Taskロール指定しててもIMDSを直接叩くSSRFの攻撃の防御にはならない。

これに対応するには

  • おとなしくFargate使え
  • どうしてもEC2で行くなら、コンテナ->IMDSのアクセスをiptablesで殺すとか

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のサービスディスカバリにも使われているので色々捗るはず。