Nginx Ingress Controller + oauth2-proxyでSSOしてるときに、特定のIPアドレスからのリクエストのみ認証をスキップさせる
タイトルに書いてある通りのことがやりたかったのだけど、結構はまったので残しておきます。 脆弱性テストのためのクローラを通すとか、必要なシチュエーションはあるのではないでしょうか。
色々試行錯誤したのですが、下記の手順でいけました。
Nginx Ingress Controllerのfowarded-forオプションを設定する
k8sクラスタの前段にCDNなどを配置している場合はまずこれが必要です。
use-forwarded-headersにTrueを設定することで、Nginx Ingress Controllerからプロキシされる先にX-Forwaded系のヘッダーがセットされます。
ConfigMap - NGINX Ingress Controller
oauth2-proxyの設定を追加する
oauth2-proxyでX-Forwaded-ForヘッダにセットされたIPを受け取るには、reverse-proxyオプションと、real-client-ip-headerオプションを指定する必要があります。(oauth2-proxyのデフォルトはX-Real-IPのため、real-client-ip-headerオプションで設定する必要があります) その上で、trusted-ipオプションを指定します。
また、気付きづらいのですが、 trusted-ipを指定し、認証がスキップされると、oauth2-proxyはそのままupstreamにリクエストをプロキシします。そのため、upstreamが指定されていないと、404を返却し、Nginx Ingress Controllerが行う認証が失敗してしまいます。この場合、Nginx Ingress Controllerは500を返します。これを回避するためには、 20xを返す upstreamを設定してあげればよいです。 (static://202 など)
ここまでのオプションをまとめるとこうなります。
--real-client-ip-header=X-Forwarded-For --reverse-proxy=true --upstream=static://202 --trusted-ip=xxx.xxx.xxx.xxx --trusted-ip=yyy.yyy.yyy.yyy --trusted-ip=zzz.zzz.zzz.zzz
参考
Nginx Ingress Controller とoauth2-proxyを利用したSSOについて blog.1q77.com
trusted_ipの挙動について github.com
2020年まとめ
仕事の区切りが悪く、年末年始感があまりなかった。今やってるプロジェクトが一区切りついたらまとめたい。(特に忙しいわけではない)
最近は仕事以外の趣味に使う時間を増やしていきたい気持ち。特に体動かす系は、今のうちにどのくらい技術習得できるかが今後の楽しみに影響しそう。 仕事、というかソフトウェア開発は体力関係なくずっと出来そうなので、気長にやっていく。
Nginx Ingress Controllerには2つの実装があるので注意
はじめに
Nginx Ingress Controllerには2つの実装があります。
Kubernetesチーム管理のkubernetes/ingress-nginx(以下ingress-nginx) github.com
Nginxチーム管理のnginx/kubernetes-ingress(以下nginx-ingress)
ingress-nginx, nginx-ingressと呼び分けているのは、両者のドキュメントで Helmを利用したインストール方法でnameとして採用されているからです。
2つの違い
Nginx側のレポジトリ にドキュメントがあります。
kubernetes-ingress/nginx-ingress-controllers.md at master · nginxinc/kubernetes-ingress · GitHub
基本的な設定は似ていますが、マニフェストの書き方が異なる箇所が多く互換性はありません。Nginxの商用製品を使う場合はnginx-ingressが必須です。
ingress-nginxに関するドキュメントは、 kubernetes.github.ioでホストされています。 kubernetes.github.io
nginx-ingressは、docs.nginx.com でホストされています。 docs.nginx.com
annotationなど設定値で検索すると両者がヒットするとこがあるので、間違えないように気を付ける必要があります。
stable/nginx-ingress について
古い記事では、下記のように stable/nginx-ingressを利用する記述がありますが、stable/nginx-ingress はDeprecatedになっており、新規で利用してはいけません
helm install stable/nginx-ingress
stable/nginx-ingress はKubernetesコミュニティの Ingress Controller なので、移行先は ingress-nginx です。
https://github.com/helm/charts/tree/master/stable/nginx-ingress
BigQueryで日付の欠損を補完したレポートを作成する
BigQueryで日付の欠損を補完したレポートを作成するときの方法です。過去何度かやったのだけどその度に調べたり思い出して対応してたのでここに残しておきます。
たとえば
テーブル1
日付 | 属性1 | 金額A |
---|---|---|
2020-07-01 | A | 500 |
2020-07-03 | B | 500 |
テーブル2
日付 | 属性1 | 金額B |
---|---|---|
2020-07-01 | B | 100 |
2020-07-02 | B | 500 |
という2つのテーブルから下記のような結果を出したいとします。(例としてはめちゃくちゃなんですが、やりたいことは察してください)
日付 | 属性1 | 金額A | 金額B |
---|---|---|---|
2020-07-01 | A | 500 | 100 |
2020-07-01 | B | ||
2020-07-02 | A | ||
2020-07-02 | B | 500 | |
2020-07-03 | A | ||
2020-07-03 | B | 500 |
日付を作成する
GENERATE_DATE_ARRAY関数で日付の配列を作成することができます。UNNEST関数で展開することでテーブルとして利用することができます。
SELECT base_date FROM UNNEST(GENERATE_DATE_ARRAY( DATE("2020-07-01"), DATE("2020-07-03"))) AS base_date
属性のパターンを作成し、クロスジョイン
属性の取り得るパターンを事前に作成し、日付とクロスジョインします。(属性のパターンは、SELECTで作成するのではなく、マスターテーブルから取得するのが正しいです)
WITH date_series AS ( SELECT base_date FROM UNNEST(GENERATE_DATE_ARRAY( DATE("2020-07-01"), DATE("2020-07-03"))) AS base_date ), dimensions AS ( SELECT "A" as attr1 UNION ALL SELECT "B" ),base AS ( SELECT base_date, attr1 FROM date_series, dimensions ) SELECT * FROM base
このSQLで下記のようなテーブルを作成することができます
date | attr1 |
---|---|
2020-07-01 | A |
2020-07-01 | B |
2020-07-02 | A |
2020-07-02 | B |
2020-07-03 | A |
2020-07-03 | B |
データテーブルとjoinする
あとは、データが入っているテーブルとJOINすれば完了です。WITH句は先ほどと同じです。
WITH date_series AS ( SELECT base_date FROM UNNEST(GENERATE_DATE_ARRAY( DATE("2020-07-01"), DATE("2020-07-03"))) AS base_date ), dimensions AS ( SELECT "A" as attr1 UNION ALL SELECT "B" ),base AS ( SELECT base_date, attr1 FROM date_series, dimensions ) SELECT base.base_date, base.attr1, テーブル1.金額A テーブル2.金額B FROM base LEFT OUTER JOIN テーブル1 ON base.base_date = テーブル1.base_date AND base.attr1 = テーブル1.attr1 LEFT OUTER JOIN テーブル2 ON base.base_date = テーブル2.base_date AND base.attr1 = テーブル2.attr1
VALORANTをインストールするとキーボードが効かなくなる場合がある
はじめに
VALORANTをインストールすると、VALORANTと一緒にインストールされるアンチチートツールVanguardによってCtrl2Capがチートツール扱いされ、ドライバエラーになり、なんとキーボードが無効化されるのでその注意喚起です。 この事象はRedditでも報告されていました。
対応方法
キーボードが無効化されるのでソフトウェアキーボードでWindowsを操作する必要があります。 ログイン画面では右下からソフトウェアキーボードを表示することができ、ログイン後はタスクバー右クリックで「タッチキーボードボタンを表示」を有効化することでソフトウェアキーボードを有効化することができます。 タスクバーからVanguardを右クリックしてUninstallを選択してアンインストール、その後OS再起動でキーボードが有効になります。
VALORANTをPlayするには
おそらく、ctrl2capをアンインストールするしか方法はありません。僕はctrl2cap無しでWindowsは扱えないのでVALORANTはしばらく見送りました。
ちなみに
キーボード故障かと思って新しいキーボード買ってしまったのですが、結果的には買い替えるよい口実になりました。
追記
アンチチートツールについてはすでに話題になってました maruhoi.com
KubernetesでPodに指定したServiceAccountを削除したいときは"default"を指定する
KubernetesでPodに指定したServiceAccountを削除したいときにはまったのでメモ
たとえばこんな感じでPodにServiceAccountを付与したとき
apiVersion: apps/v1 kind: Deployment metadata: namespace: default spec: replicas: 2 selector: matchLabels: app: test template: metadata: labels: app: test spec: serviceAccountName: "foo" containers: - name: test image: busybox command: - sleep - "600"
サービスアカウントを指定を削除するために"serviceAccountName"要素を削除したマニフェストを作って適用したとする
apiVersion: apps/v1 kind: Deployment metadata: namespace: default spec: replicas: 2 selector: matchLabels: app: test template: metadata: labels: app: test spec: containers: - name: test image: busybox command: - sleep - "600"
この場合、 serviceAccountName: "foo" は残ってしまう。未指定の場合は過去の設定をそのまま引き継いでしまう。明示的にserviceAccountをリセットする必要がある。
このときに、 serviceAccountName: "" のように空白を指定すると結局無視されてしまうので注意が必要。serviceAccountのデフォルトは"default"というserviceAccountなので、 serviceAccountName: "default" を指定する必要がある。
これが正解。
apiVersion: apps/v1 kind: Deployment metadata: namespace: default spec: replicas: 2 selector: matchLabels: app: test template: metadata: labels: app: test spec: serviceAccountName: "default" containers: - name: test image: busybox command: - sleep - "600"
ちなみに、ServiceAccountNameを指定すると、ServiceAccountNameだけではなく、ServiceAccountという要素にも値が設定される。これはServiceAccountの方は既にDeprectedになっている古いAPIで互換性のために値がコピーされているだけなので気にしなくて良い。PodのSpecを見ると確認することができる。
https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#pod-v1-core
GCPのPubSubをGoでsubscribeするときにgraceful shutdownのようなことをする
GCPのPubSubのTopicをGoでsubscribeするとき、ライブラリ( https://github.com/googleapis/google-cloud-go ) を使えば下記のように簡単に記述できます
client, err := pubsub.NewClient(ctx, "project-id") if err != nil { log.Fatal(err) } sub := client.Subscription("subscription1") err = sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) { fmt.Println(m.Data) // process message m.Ack() // Acknowledge that we've consumed the message. }) if err != nil { log.Println(err) }
しかしこれではTopicのメッセージを処理中に、コンテナが停止するなど何らかの理由でサーバーが停止するときに処理は行なっているもののAckを返していない、という状態になってしまいます。
シグナルハンドリングを行い、停止までに一定の猶予を設けることで、webサーバでよくあるgraceful shutdownのような挙動にするには、こんな感じにします
cctx, cancel := context.WithCancel(ctx) sub := client.Subscription("subscription1") err := sub.Receive(cctx, func(ctx context.Context, msg *pubsub.Message) { fmt.Println(m.Data) // process message m.Ack() // Acknowledge that we've consumed the message. } quit := make(chan os.Signal, 1) signal.Notify(quit, os.Interrupt, syscall.SIGTERM) <-quit // Start shutdown process cancel() // Waiting pubsub receive shutdown 2 seconds.. time.Sleep(2000 * time.Millisecond)
Receive関数渡したcontextを、SIGINTをハンドリング待ってから、cancelし、その完了を一定期間待つことで実現できます。この例では2秒待っていますがどのぐらい待つのかは処理内容によって考える必要があります。 メッセージの処理に時間かかり、処理中に設定したタイムアウト期間を越えることが想定される場合は、Receive関数に渡す無名関数内で必要に応じてcontext.Doneを確認するとよいと思います(あまりないと思いますが)